Files
userscripts/improved_redgifs.user.js
T
big-duckie 06d85089ea Update improved_redgifs.user.js
Added check to make sure the token is valid. If not, refresh token.
2025-12-22 01:28:00 -07:00

204 lines
7.1 KiB
JavaScript

// ==UserScript==
// @name Improved RedGIFs
// @namespace BigDuckie.ImprovedRedgifs
// @version 2025.12.22
// @description Fix a few annoying "features" and add an easy download button.
// @icon https://www.google.com/s2/favicons?sz=64&domain=redgifs.com
// @author Big Duckie
// @copyright 2025, Big Duckie (https://bigduckie.dev)
// @license MIT
// @match https://*.redgifs.com/
// @match https://*.redgifs.com/watch/*
// @match https://*.redgifs.com/niches/*
// @match https://*.redgifs.com/users/*
// @match https://*.redgifs.com/ifr/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @require https://raw.githubusercontent.com/pie6k/jquery.initialize/master/jquery.initialize.min.js
// @grant GM_addStyle
// @grant GM_download
// ==/UserScript==
(function () {
'use strict';
GM_addStyle(`
.RGButton {
background: 0 0;
border: none;
cursor: pointer;
color: var(--white);
font: var(--s1-medium);
text-shadow: var(--black) 1px 0 5px;
text-align: center;
}`);
jQuery.noConflict();
const play_svg = () => jQuery(document.createElementNS("http://www.w3.org/2000/svg", "svg")).attr({
width: 24,
height: 24,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
stroke_width: 2,
stroke_linecap: "round",
stroke_linejoin: "round"
}).html(`<polygon points="5 3 19 12 5 21 5 3"></polygon>`);
const pause_svg = () => jQuery(document.createElementNS("http://www.w3.org/2000/svg", "svg")).attr({
width: 24,
height: 24,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
stroke_width: 2,
stroke_linecap: "round",
stroke_linejoin: "round"
}).html(`<rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect>`);
const download_svg = () => jQuery(document.createElementNS("http://www.w3.org/2000/svg", "svg")).attr({
width: 24,
height: 24,
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
stroke_width: 2,
stroke_linecap: "round",
stroke_linejoin: "round"
}).html(`<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line>`);
// Remove overlayer
jQuery.initialize(".Player-OverLayer", function () {
this.remove();
});
// Remove videoLink href
jQuery.initialize(".videoLink", function() {
jQuery(this).removeAttr("href");
});
// --- Watch pages ---
jQuery.initialize(".sideBar", async function () {
console.debug("detected watch page gif");
const sidebar = jQuery(this);
const root = sidebar.parents(".GifPreview");
// Add play pause button
sidebar.prepend(
jQuery("<li>", {
class: "sideBarItem"
}).append(
jQuery("<button>", {
class: "PPButton RGButton",
"aria-label": "pause play button"
}).append(pause_svg()).on("click", () => {
togglePlayer(root.find(".Player-Video>video")[0]);
})
));
// Add download button
sidebar.children(".sideBarItem:has(.FSButton)").after(
jQuery("<li>", {
class: "sideBarItem"
}).append(
jQuery("<button>", {
class: "DLButton RGButton",
"aria-label": "download button"
}).append(download_svg()).on("click", () => {
download(root.attr("data-feed-item-id"));
})
));
}, { target: jQuery(".previewFeed")[0] });
// Add callback to change pause play button svg.
jQuery.initialize(".Player-Video>video", function () {
const video = jQuery(this);
const root = video.parents(".GifPreview");
video.on("play", function () {
root.find(".PPButton>svg").replaceWith(play_svg());
}).on("pause", function () {
root.find(".PPButton>svg").replaceWith(pause_svg());
});
}, { target: jQuery(".previewFeed")[0] });
// --- Watch Pages ---
// --- IFR Pages (Embedded) ---
jQuery.initialize(".sidebar", function () {
console.debug("detected embedded gif");
const sidebar = jQuery(this);
const root = sidebar.parents(".embeddedPlayer");
// Add play pause button
sidebar.prepend(
jQuery("<li>", {
class: "item"
}).append(
jQuery("<button>", {
class: "PPButton RGButton",
"aria-label": "pause play button"
}).append(pause_svg()).on("click", () => {
togglePlayer(root.find(".videoLink>video")[0]);
})
));
// Add download button
sidebar.children(".item:has(.FSButton)").after(
jQuery("<li>", {
class: "item"
}).append(
jQuery("<button>", {
class: "DLButton RGButton",
"aria-label": "download button"
}).append(download_svg()).on("click", () => {
download(window.location.pathname.split("/")[2]);
})
));
}, { target: jQuery(".routeWrapper")[0] });
// Add callback to change pause play button svg.
jQuery.initialize(".videoLink>video", function () {
const video = jQuery(this);
const root = video.parents(".embeddedPlayer");
video.on("play", function () {
root.find(".PPButton>svg").replaceWith(play_svg());
}).on("pause", function () {
root.find(".PPButton>svg").replaceWith(pause_svg());
});
}, { target: jQuery(".routeWrapper")[0] });
// --- IFR Pages ---
// --- Helper Functions ---
async function togglePlayer(video) {
video.paused ? video.play() : video.pause();
}
async function getToken() {
let token = localStorage.getItem("userscript_access_token") ?? (await (await fetch("https://api.redgifs.com/v2/auth/temporary")).json()).token;
let tokenData = JSON.parse(atob(token.split(".")[1].replace('-', '+').replace('_', '/')));
let unixtime = Math.floor(Date.now() / 1000);
if (unixtime > tokenData.exp) {
token = (await (await fetch("https://api.redgifs.com/v2/auth/temporary")).json()).token;
}
localStorage.setItem("userscript_access_token", token);
return token
}
async function download(id) {
let token = await getToken();
let resp = await fetch(`https://api.redgifs.com/v2/gifs/${id}`, { headers: { Authorization: `Bearer ${token}` } });
if (!resp.ok) {
localStorage.removeItem("userscript_access_token");
await download(id);
return
}
let gif = await resp.json();
let url = gif.gif.urls.hd;
GM_download(url, url.split("/").pop().split("?")[0]);
}
})();