// ==UserScript== // @name Chaturbate 缩略图放大 2 倍 // @namespace https://chaturbate.com/ // @version 0.1.1 // @description 将房间列表缩略图按宽高 2 倍放大 // @match https://chaturbate.com/* // @match https://*.chaturbate.com/* // @run-at document-start // @grant none // ==/UserScript== (function () { "use strict"; // ========== 配置项 ========== // 放大倍数(宽度和高度都会按此倍数放大) const SCALE = 1.5; // 调试开关(true: 输出调试信息,false: 静默运行) const DEBUG = false; // =========================== // ========== 内部常量 ========== const SCALED_ATTR = "data-tm-scaled"; const BASE_W_ATTR = "data-tm-base-w"; const BASE_H_ATTR = "data-tm-base-h"; // =========================== // 提前注入 CSS,避免闪烁 const injectCSS = () => { const style = document.createElement("style"); style.id = "tm-thumb-scale-style"; const css = ` img.room_thumbnail:not([${SCALED_ATTR}="1"]) { opacity: 0; transition: opacity 0.1s; } img.room_thumbnail[${SCALED_ATTR}="1"] { opacity: 1; } ul.list, ul.list.endless_page_template { display: flex !important; flex-wrap: wrap !important; gap: 0 !important; } `; style.textContent = css; const inject = () => { if (!document.getElementById("tm-thumb-scale-style")) { (document.head || document.documentElement).appendChild(style); } }; if (document.head) { inject(); } else { document.addEventListener("DOMContentLoaded", inject); } }; injectCSS(); const relaxKnownOuter = () => { // 确保列表容器能容纳更大的卡片 document.querySelectorAll("ul.list, ul.list.endless_page_template").forEach((el) => { el.style.width = "100%"; el.style.maxWidth = "none"; el.style.boxSizing = "border-box"; el.style.overflow = "visible"; }); }; const scale = () => { if (DEBUG) console.log("[tm] running on", location.href); relaxKnownOuter(); const items = document.querySelectorAll("a.room_thumbnail_container"); if (DEBUG) console.log("[tm] containers:", items.length); items.forEach((container) => { const img = container.querySelector("img.room_thumbnail"); if (DEBUG && !img) console.log("[tm] no img in container", container); if (!img) return; const attrW = parseFloat(img.getAttribute("width")); const attrH = parseFloat(img.getAttribute("height")); const baseW = parseFloat(img.getAttribute(BASE_W_ATTR)) || attrW; const baseH = parseFloat(img.getAttribute(BASE_H_ATTR)) || attrH; if (!baseW || !baseH) return; if (!img.getAttribute(BASE_W_ATTR) || !img.getAttribute(BASE_H_ATTR)) { img.setAttribute(BASE_W_ATTR, String(baseW)); img.setAttribute(BASE_H_ATTR, String(baseH)); } const scaledW = baseW * SCALE; const scaledH = baseH * SCALE; if (DEBUG) console.log("[tm] img size:", baseW, baseH, img); img.setAttribute("width", String(scaledW)); img.setAttribute("height", String(scaledH)); img.setAttribute(SCALED_ATTR, "1"); img.style.width = `${scaledW}px`; img.style.height = `${scaledH}px`; img.style.maxWidth = "none"; img.style.maxHeight = "none"; img.style.display = "block"; img.style.opacity = "1"; container.style.width = `${scaledW}px`; container.style.height = `${scaledH}px`; container.style.minWidth = `${scaledW}px`; container.style.maxWidth = `${scaledW}px`; container.style.minHeight = `${scaledH}px`; container.style.overflow = "visible"; container.style.display = "block"; container.style.boxSizing = "border-box"; container.style.flexShrink = "0"; const card = container.closest("li.roomCard"); if (card) { card.style.width = `${scaledW}px`; card.style.minWidth = `${scaledW}px`; card.style.maxWidth = `${scaledW}px`; card.style.minHeight = `${scaledH}px`; card.style.height = "auto"; card.style.overflow = "visible"; card.style.flexShrink = "0"; card.style.flexGrow = "0"; card.style.flexBasis = `${scaledW}px`; card.style.boxSizing = "border-box"; } }); }; const init = () => { if (DEBUG) { const thumbs = document.querySelectorAll("img.room_thumbnail"); console.log("[tm] thumbnails:", thumbs.length, Array.from(thumbs)); } scale(); }; // 立即执行(如果 DOM 已就绪) if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } // 延迟重试,处理动态加载 window.setTimeout(scale, 100); window.setTimeout(scale, 300); window.setTimeout(scale, 600); window.setTimeout(scale, 1000); window.setTimeout(scale, 2000); // MutationObserver 监听动态添加的内容 const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.nodeType === 1 && ( node.matches?.("a.room_thumbnail_container, li.roomCard") || node.querySelector?.("a.room_thumbnail_container") )) { if (DEBUG) console.log("[tm] new content detected, scaling..."); scale(); return; } } } }); // 开始观察 const startObserving = () => { const target = document.body || document.documentElement; if (target) { observer.observe(target, { childList: true, subtree: true }); if (DEBUG) console.log("[tm] MutationObserver started"); } }; if (document.body) { startObserving(); } else { document.addEventListener("DOMContentLoaded", startObserving); } })();