Untitled
unknown
javascript
7 days ago
13 kB
24
No Index
// ==UserScript==
// @name GOG Games Hider + Control Panel + Fetch Limit Overwrite
// @namespace https://gog.com/
// @version 2.3
// @description Hide/dim games, Early Access filter, hide button, panel toggle, and fetch limit overwrite
// @match https://www.gog.com/en/*
// @grant none
// ==/UserScript==
(function () {
"use strict";
const STORAGE_KEY = "hiddenGogGames";
const MODE_KEY = "gogHideMode"; // 'none' | 'dim' | 'hide'
const OPACITY_KEY = "gogDimOpacity";
const PANEL_OPEN_KEY = "gogPanelOpen";
const LIMIT_TOGGLE_KEY = "gogLimitToggle";
const LIMIT_VALUE_KEY = "gogLimitValue";
// Load settings
const loadJSON = (key, fallback) => {
try {
const v = localStorage.getItem(key);
return v === null ? fallback : JSON.parse(v);
} catch {
return fallback;
}
};
const saveJSON = (key, val) => localStorage.setItem(key, JSON.stringify(val));
const hiddenIds = new Set(loadJSON(STORAGE_KEY, []));
let mode = localStorage.getItem(MODE_KEY) || "dim";
let dimOpacity = parseFloat(localStorage.getItem(OPACITY_KEY)) || 0.1;
let panelOpen = localStorage.getItem(PANEL_OPEN_KEY) !== "false";
let limitToggle = localStorage.getItem(LIMIT_TOGGLE_KEY) === "true";
let limitValue = parseInt(localStorage.getItem(LIMIT_VALUE_KEY)) || 100;
const saveMode = (v) => localStorage.setItem(MODE_KEY, v);
const saveOpacity = (v) => localStorage.setItem(OPACITY_KEY, String(v));
const savePanelOpen = (v) =>
localStorage.setItem(PANEL_OPEN_KEY, v ? "true" : "false");
const saveLimitToggle = (v) =>
localStorage.setItem(LIMIT_TOGGLE_KEY, v ? "true" : "false");
const saveLimitValue = (v) =>
localStorage.setItem(LIMIT_VALUE_KEY, String(v));
// --- Style application ---
const applyStyle = (tile, type) => {
const parent = tile.parentElement;
if (!parent) return;
if (type === "hide") parent.style.display = "none";
else if (type === "dim") {
parent.style.display = "";
parent.style.opacity = String(dimOpacity);
}
};
const resetStyle = (tile) => {
const p = tile.parentElement;
if (!p) return;
p.style.display = "";
p.style.opacity = "";
};
const isEarlyAccess = (tile) =>
tile.innerText.toLowerCase().includes("early access");
// --- Hide Button ---
const createHideButton = (tile, id) => {
if (tile.querySelector(".gog-hide-btn")) return;
const btn = document.createElement("button");
btn.textContent = "Hide";
btn.className = "gog-hide-btn";
Object.assign(btn.style, {
position: "absolute",
top: "4px",
right: "4px",
zIndex: "10000000",
background: "rgba(0,0,0,0.6)",
color: "white",
border: "none",
padding: "2px 6px",
borderRadius: "4px",
cursor: "pointer",
fontSize: "18px",
});
btn.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
hiddenIds.add(id);
saveJSON(STORAGE_KEY, [...hiddenIds]);
if (mode !== "none") applyStyle(tile, mode);
btn.remove();
});
if (getComputedStyle(tile).position === "static")
tile.style.position = "relative";
tile.appendChild(btn);
};
// --- Unhide Button (for already hidden tiles) ---
const createUnhideButton = (tile, id) => {
if (tile.querySelector(".gog-unhide-btn")) return;
const btn = document.createElement("button");
btn.textContent = "Unhide";
btn.className = "gog-unhide-btn";
Object.assign(btn.style, {
position: "absolute",
bottom: "4px",
left: "4px",
zIndex: "10000000",
background: "rgba(0,0,0,0.6)",
color: "white",
border: "none",
padding: "2px 6px",
borderRadius: "4px",
cursor: "pointer",
fontSize: "14px",
});
btn.addEventListener("click", (e) => {
e.stopPropagation();
e.preventDefault();
hiddenIds.delete(id);
saveJSON(STORAGE_KEY, [...hiddenIds]);
// Reset styles and remove button
resetStyle(tile);
btn.remove();
// Recreate the hide button
createHideButton(tile, id);
});
if (getComputedStyle(tile).position === "static")
tile.style.position = "relative";
tile.appendChild(btn);
};
// --- Process Tiles ---
const processGameTiles = () => {
document.querySelectorAll("[data-product-id]").forEach((tile) => {
const id = tile.getAttribute("data-product-id");
if (!id) return;
resetStyle(tile);
if (hiddenIds.has(id)) {
// Apply dim/hide effect if mode is active
if (mode !== "none") {
applyStyle(tile, mode);
}
// Only show Unhide button when mode = "none"
if (mode === "none") {
createUnhideButton(tile, id);
}
return;
}
// Visible item → ensure hide button exists
createHideButton(tile, id);
});
};
const observer = new MutationObserver(processGameTiles);
observer.observe(document.body, { childList: true, subtree: true });
processGameTiles();
// --- Panel ---
const panel = document.createElement("div");
Object.assign(panel.style, {
position: "fixed",
top: "60px",
right: "10px",
background: "rgba(30,30,30,0.92)",
color: "white",
padding: "10px",
borderRadius: "8px",
zIndex: "999999",
fontSize: "14px",
display: "flex",
flexDirection: "column",
gap: "8px",
alignItems: "center",
minWidth: "190px",
boxShadow: "0 6px 18px rgba(0,0,0,0.4)",
opacity: panelOpen ? "1" : "0",
pointerEvents: panelOpen ? "auto" : "none",
transition: "opacity 0.25s ease",
});
// --- Toggle button ---
const toggleBtn = document.createElement("button");
toggleBtn.textContent = "?";
Object.assign(toggleBtn.style, {
position: "fixed",
top: "10px",
right: "10px",
width: "34px",
height: "34px",
background: "rgba(50,50,50,0.95)",
borderRadius: "50%",
border: "none",
cursor: "pointer",
fontSize: "20px",
color: "white",
zIndex: "1000000",
boxShadow: "0 4px 12px rgba(0,0,0,0.4)",
});
toggleBtn.addEventListener("click", () => {
panelOpen = !panelOpen;
savePanelOpen(panelOpen);
panel.style.opacity = panelOpen ? "1" : "0";
panel.style.pointerEvents = panelOpen ? "auto" : "none";
});
// --- Mode dropdown ---
const modeLabel = document.createElement("div");
modeLabel.textContent = "Filter Mode:";
modeLabel.style.alignSelf = "flex-start";
const modeSelect = document.createElement("select");
["none", "dim", "hide"].forEach((opt) => {
const o = document.createElement("option");
o.value = opt;
o.textContent =
opt === "none" ? "No Filter" : opt === "dim" ? "Dim" : "Hide";
if (opt === mode) o.selected = true;
modeSelect.appendChild(o);
});
modeSelect.style.padding = "6px";
modeSelect.style.width = "100%";
modeSelect.addEventListener("change", () => {
mode = modeSelect.value;
saveMode(mode);
updateOpacityUI();
// Remove all unhide buttons when mode ≠ "none"
if (mode !== "none") {
document
.querySelectorAll(".gog-unhide-btn")
.forEach((btn) => btn.remove());
}
processGameTiles();
});
const opacityContainer = document.createElement("div");
opacityContainer.style.display = mode === "dim" ? "flex" : "none";
opacityContainer.style.flexDirection = "column";
opacityContainer.style.width = "100%";
const opacityLabel = document.createElement("div");
opacityLabel.textContent = `Dim Opacity: ${dimOpacity}`;
const opacitySlider = document.createElement("input");
opacitySlider.type = "range";
opacitySlider.min = "0.05";
opacitySlider.max = "1";
opacitySlider.step = "0.01";
opacitySlider.value = dimOpacity;
opacitySlider.addEventListener("input", () => {
dimOpacity = parseFloat(opacitySlider.value);
opacityLabel.textContent = `Dim Opacity: ${dimOpacity}`;
saveOpacity(dimOpacity);
processGameTiles();
});
opacityContainer.appendChild(opacityLabel);
opacityContainer.appendChild(opacitySlider);
const updateOpacityUI = () => {
opacityContainer.style.display = mode === "dim" ? "flex" : "none";
};
// --- Export / Import hidden IDs ---
const exportBtn = document.createElement("button");
exportBtn.textContent = "Export Hidden IDs";
Object.assign(exportBtn.style, {
padding: "6px 8px",
background: "#007acc",
color: "white",
border: "none",
cursor: "pointer",
borderRadius: "6px",
width: "100%",
});
exportBtn.addEventListener("click", () => {
const blob = new Blob([JSON.stringify([...hiddenIds], null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "hidden_gog_games.json";
a.click();
URL.revokeObjectURL(url);
});
const importBtn = document.createElement("button");
importBtn.textContent = "Import Hidden IDs";
Object.assign(importBtn.style, {
padding: "6px 8px",
background: "#28a745",
color: "white",
border: "none",
cursor: "pointer",
borderRadius: "6px",
width: "100%",
});
importBtn.addEventListener("click", () => {
const json = prompt("Paste the JSON array of hidden IDs:");
if (!json) return;
try {
const imported = JSON.parse(json);
if (Array.isArray(imported)) {
imported.forEach((id) => hiddenIds.add(id));
saveJSON(STORAGE_KEY, [...hiddenIds]);
processGameTiles();
alert("Imported successfully!");
} else {
alert("Invalid format: expected JSON array.");
}
} catch {
alert("Invalid JSON.");
}
});
// --- Limit Overwrite Toggle ---
const limitContainer = document.createElement("div");
limitContainer.style.display = "flex";
limitContainer.style.flexDirection = "column";
limitContainer.style.width = "100%";
limitContainer.style.gap = "8px";
const limitToggleBtn = document.createElement("button");
limitToggleBtn.textContent = limitToggle
? "Overwrite Limit: ON"
: "Overwrite Limit: OFF";
Object.assign(limitToggleBtn.style, {
padding: "6px 8px",
borderRadius: "6px",
border: "none",
cursor: "pointer",
width: "100%",
background: limitToggle ? "#116622" : "#444",
color: "white",
});
const limitInput = document.createElement("input");
limitInput.type = "number";
limitInput.min = "10";
limitInput.max = "100";
limitInput.value = limitValue;
limitInput.style.width = "100%";
limitInput.style.display = limitToggle ? "block" : "none";
const limitNote = document.createElement("div");
limitNote.textContent =
"Limit must be between 10 and 100 (needs refresh to apply)";
limitNote.style.fontSize = "12px";
limitNote.style.color = "#ccc";
limitNote.style.display = limitToggle ? "block" : "none";
limitNote.style.maxWidth = "190px";
limitToggleBtn.addEventListener("click", () => {
limitToggle = !limitToggle;
saveLimitToggle(limitToggle);
limitToggleBtn.textContent = limitToggle
? "Overwrite Limit: ON"
: "Overwrite Limit: OFF";
limitToggleBtn.style.background = limitToggle ? "#116622" : "#444";
limitInput.style.display = limitToggle ? "block" : "none";
limitNote.style.display = limitToggle ? "block" : "none";
});
limitInput.addEventListener("input", () => {
let val = parseInt(limitInput.value);
if (val < 10) val = 10;
if (val > 100) val = 100;
limitInput.value = val;
limitValue = val;
saveLimitValue(limitValue);
});
limitContainer.appendChild(limitToggleBtn);
limitContainer.appendChild(limitInput);
limitContainer.appendChild(limitNote);
// --- Assemble panel ---
panel.appendChild(modeLabel);
panel.appendChild(modeSelect);
panel.appendChild(opacityContainer);
panel.appendChild(exportBtn);
panel.appendChild(importBtn);
panel.appendChild(limitContainer);
document.body.appendChild(panel);
document.body.appendChild(toggleBtn);
// --- Intercept fetch/XHR ---
const originalFetch = window.fetch;
window.fetch = function (input, init) {
let url = typeof input === "string" ? input : input.url;
if (limitToggle && url.includes("catalog.gog.com/v1/catalog")) {
const u = new URL(url);
u.searchParams.set("limit", String(limitValue));
input = u.toString();
}
return originalFetch(input, init);
};
const open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (method, url) {
if (limitToggle && url.includes("catalog.gog.com/v1/catalog")) {
const u = new URL(url);
u.searchParams.set("limit", String(limitValue));
url = u.toString();
}
return open.apply(this, [method, url]);
};
})();
Editor is loading...
Leave a Comment