Files
Tampermonkey_scripts/Chaturbate/chaturbate-thumbnails-2x.user.js
2026-01-25 08:42:40 +08:00

186 lines
5.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ==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);
}
})();