234 lines
7.0 KiB
JavaScript
234 lines
7.0 KiB
JavaScript
// ==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 = 2;
|
||
const SCALED_ATTR = "data-tm-scaled";
|
||
const BASE_W_ATTR = "data-tm-base-w";
|
||
const BASE_H_ATTR = "data-tm-base-h";
|
||
const DEBUG = true;
|
||
|
||
// 提前注入 CSS,避免闪烁
|
||
const injectCSS = () => {
|
||
const style = document.createElement("style");
|
||
style.id = "tm-thumb-scale-style";
|
||
style.textContent = `
|
||
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;
|
||
}
|
||
ul.list li.roomCard,
|
||
ul.list.endless_page_template li.roomCard {
|
||
position: relative !important;
|
||
flex-shrink: 0 !important;
|
||
flex-grow: 0 !important;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
`;
|
||
if (document.head) {
|
||
if (!document.getElementById("tm-thumb-scale-style")) {
|
||
document.head.appendChild(style);
|
||
}
|
||
} else {
|
||
document.addEventListener("DOMContentLoaded", () => {
|
||
if (!document.getElementById("tm-thumb-scale-style")) {
|
||
document.head.appendChild(style);
|
||
}
|
||
});
|
||
}
|
||
};
|
||
injectCSS();
|
||
|
||
const relaxBox = (el) => {
|
||
if (!el) return;
|
||
el.style.overflow = "visible";
|
||
el.style.maxWidth = "none";
|
||
};
|
||
|
||
const relaxAncestors = (el, depth = 6) => {
|
||
let current = el?.parentElement;
|
||
let steps = 0;
|
||
while (current && steps < depth && current !== document.body) {
|
||
relaxBox(current);
|
||
current = current.parentElement;
|
||
steps += 1;
|
||
}
|
||
};
|
||
|
||
const relaxKnownOuter = () => {
|
||
const selectors = [
|
||
"#roomlist_content_wrapper",
|
||
"#roomlist_root",
|
||
".BaseRoomContents",
|
||
"[data-testid='room-list']",
|
||
"ul.list",
|
||
"ul.list.endless_page_template",
|
||
];
|
||
selectors.forEach((selector) => {
|
||
document.querySelectorAll(selector).forEach((el) => {
|
||
relaxBox(el);
|
||
// 如果是列表容器,确保布局能容纳更大的卡片
|
||
if (el.matches?.("ul.list, ul.list.endless_page_template")) {
|
||
el.style.width = "100%";
|
||
el.style.maxWidth = "none";
|
||
el.style.boxSizing = "border-box";
|
||
el.style.display = "flex";
|
||
el.style.flexWrap = "wrap";
|
||
el.style.gap = "0";
|
||
}
|
||
});
|
||
});
|
||
};
|
||
|
||
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.maxHeight = "none";
|
||
container.style.overflow = "visible";
|
||
container.style.display = "block";
|
||
container.style.position = "relative";
|
||
container.style.boxSizing = "border-box";
|
||
container.style.flexShrink = "0";
|
||
container.style.margin = "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.position = "relative";
|
||
card.style.flexShrink = "0";
|
||
card.style.flexGrow = "0";
|
||
card.style.flexBasis = `${scaledW}px`;
|
||
card.style.boxSizing = "border-box";
|
||
card.style.margin = "0";
|
||
card.style.padding = "0";
|
||
relaxAncestors(card);
|
||
}
|
||
relaxAncestors(container);
|
||
});
|
||
};
|
||
|
||
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) => {
|
||
let shouldScale = false;
|
||
mutations.forEach((mutation) => {
|
||
mutation.addedNodes.forEach((node) => {
|
||
if (node.nodeType === 1) {
|
||
if (
|
||
node.matches?.("a.room_thumbnail_container") ||
|
||
node.matches?.("li.roomCard") ||
|
||
node.querySelector?.("a.room_thumbnail_container")
|
||
) {
|
||
shouldScale = true;
|
||
}
|
||
}
|
||
});
|
||
});
|
||
if (shouldScale) {
|
||
if (DEBUG) console.log("[tm] new content detected, scaling...");
|
||
scale();
|
||
}
|
||
});
|
||
|
||
// 开始观察
|
||
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);
|
||
}
|
||
})();
|