Fix followed page thumbnail scaling

This commit is contained in:
2026-05-27 14:49:00 +08:00
parent ea532949c0
commit faec1140e7
2 changed files with 37 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Chaturbate 缩略图放大 2 倍 // @name Chaturbate 缩略图放大 2 倍
// @namespace https://chaturbate.com/ // @namespace https://chaturbate.com/
// @version 0.10.7 // @version 0.11.0
// @description 放大当前 Chaturbate 房间列表、发现页轮播、关注下拉与悬停预览缩略图 // @description 放大当前 Chaturbate 房间列表、发现页轮播、关注下拉与悬停预览缩略图
// @match https://chaturbate.com/* // @match https://chaturbate.com/*
// @match https://*.chaturbate.com/* // @match https://*.chaturbate.com/*
@@ -58,11 +58,11 @@
const DEBUG = true; const DEBUG = true;
// =========================== // ===========================
const VER = "0.10.7"; const VER = "0.11.0";
// 缓存只保存“站点原始尺寸”,不要保存乘过倍率后的尺寸。 // 缓存只保存“站点原始尺寸”,不要保存乘过倍率后的尺寸。
// 这样以后只改 THUMBNAIL_SCALE / CARD_HEIGHT_SCALE 时,可以让缓存自动失效并重新探测,避免旧倍率污染新布局。 // 这样以后只改 THUMBNAIL_SCALE / CARD_HEIGHT_SCALE 时,可以让缓存自动失效并重新探测,避免旧倍率污染新布局。
const CACHE_KEY = "tm-thumb-scale:size-cache:v7"; const CACHE_KEY = "tm-thumb-scale:size-cache:v9";
const CACHE_SCHEMA = 7; const CACHE_SCHEMA = 9;
const log = (...args) => { const log = (...args) => {
if (DEBUG) console.log("[tm-thumb-scale]", ...args); if (DEBUG) console.log("[tm-thumb-scale]", ...args);
}; };
@@ -87,8 +87,8 @@
/* /*
* home 模块: * home 模块:
* - 适用范围:主页、分类页等普通 #roomlist_root 房间列表。 * - 适用范围:主页、分类页等普通 #roomlist_root 房间列表。
* - 探测值:卡片原始宽高 + 缩略图原始宽高 * - 探测值:卡片原始宽
* - 放大方式:卡片和缩略图分别按自己的原始宽高乘倍率 * - 放大方式:只放大 grid 列宽;缩略图保持 100% / auto跟随卡片自然缩放
* - 注意:关注页列表单独用 followingList 模块,不和 home 共用缓存。 * - 注意:关注页列表单独用 followingList 模块,不和 home 共用缓存。
*/ */
html[data-tm-thumb-scale-home="1"] #main #roomlist_root ul.RoomCardGrid, html[data-tm-thumb-scale-home="1"] #main #roomlist_root ul.RoomCardGrid,
@@ -108,14 +108,11 @@
width: auto !important; width: auto !important;
min-width: 0 !important; min-width: 0 !important;
max-width: none !important; max-width: none !important;
height: var(--tm-thumb-home-card-height) !important;
min-height: var(--tm-thumb-home-card-height) !important;
max-height: var(--tm-thumb-home-card-height) !important;
} }
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail, html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container { html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
width: var(--tm-thumb-home-thumb-width) !important; width: 100% !important;
height: var(--tm-thumb-home-thumb-height) !important; height: auto !important;
display: block !important; display: block !important;
max-width: none !important; max-width: none !important;
box-sizing: border-box !important; box-sizing: border-box !important;
@@ -123,8 +120,8 @@
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail img, html[data-tm-thumb-scale-home="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail img,
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img, html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail { html[data-tm-thumb-scale-home="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
width: var(--tm-thumb-home-thumb-width) !important; width: 100% !important;
height: var(--tm-thumb-home-thumb-height) !important; height: auto !important;
/* block 可以消掉图片 inline baseline 带来的细小空隙。 */ /* block 可以消掉图片 inline baseline 带来的细小空隙。 */
display: block !important; display: block !important;
max-width: none !important; max-width: none !important;
@@ -137,7 +134,9 @@
* - 适用范围:/followed-cams/ 关注页里的 #roomlist_root 房间列表。 * - 适用范围:/followed-cams/ 关注页里的 #roomlist_root 房间列表。
* - 为什么和 home 分开:两者 DOM 选择器很像,但站点可能给不同页面不同原始列宽。 * - 为什么和 home 分开:两者 DOM 选择器很像,但站点可能给不同页面不同原始列宽。
* - 缓存也单独存 followingList避免首页探测值污染关注页。 * - 缓存也单独存 followingList避免首页探测值污染关注页。
* - 探测值同样是 card 宽高 + thumb 宽高四项 * - 和 home 一样只放大 grid 最小列宽;图片跟随卡片自然缩放
* - 注意:这里仍保留 minmax(..., 1fr),让站点原来的整行拉伸/对齐逻辑继续工作。
* 不能改成固定列宽,否则宽屏下会出现大空洞,看起来比原站更乱。
*/ */
html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.RoomCardGrid, html[data-tm-thumb-scale-following-list="1"] #main #roomlist_root ul.RoomCardGrid,
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid, html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid,
@@ -152,14 +151,11 @@
width: auto !important; width: auto !important;
min-width: 0 !important; min-width: 0 !important;
max-width: none !important; max-width: none !important;
height: var(--tm-thumb-following-list-card-height) !important;
min-height: var(--tm-thumb-following-list-card-height) !important;
max-height: var(--tm-thumb-following-list-card-height) !important;
} }
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail, html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail,
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container { html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container {
width: var(--tm-thumb-following-list-thumb-width) !important; width: 100% !important;
height: var(--tm-thumb-following-list-thumb-height) !important; height: auto !important;
display: block !important; display: block !important;
max-width: none !important; max-width: none !important;
box-sizing: border-box !important; box-sizing: border-box !important;
@@ -167,8 +163,8 @@
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail img, html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.RoomCardGrid .RoomCardThumbnail img,
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img, html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail_container img,
html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail { html[data-tm-thumb-scale-following-list="1"] #roomlist_root ul.list:has(li.roomCard) .room_thumbnail {
width: var(--tm-thumb-following-list-thumb-width) !important; width: 100% !important;
height: var(--tm-thumb-following-list-thumb-height) !important; height: auto !important;
display: block !important; display: block !important;
max-width: none !important; max-width: none !important;
box-sizing: border-box !important; box-sizing: border-box !important;
@@ -514,20 +510,10 @@
// 缓存命中时直接写 CSS 变量并打开模块开关。 // 缓存命中时直接写 CSS 变量并打开模块开关。
// 这里仍然通过 setVar() 乘倍率,因此缓存中始终保持原始尺寸。 // 这里仍然通过 setVar() 乘倍率,因此缓存中始终保持原始尺寸。
if (name === "home" && !isFollowingListPage() && isRect(data.homeCard) && isRect(data.homeThumb)) { if (name === "home" && !isFollowingListPage() && isRect(data.homeCard)) {
moduleReady.home = [ moduleReady.home = setVar("--tm-thumb-home-width", data.homeCard.width);
setVar("--tm-thumb-home-width", data.homeCard.width), } else if (name === "followingList" && isFollowingListPage() && isRect(data.followingListCard)) {
setCardHeightVar("--tm-thumb-home-card-height", data.homeCard.height), moduleReady.followingList = setVar("--tm-thumb-following-list-width", data.followingListCard.width);
setVar("--tm-thumb-home-thumb-width", data.homeThumb.width),
setVar("--tm-thumb-home-thumb-height", data.homeThumb.height),
].every(Boolean);
} else if (name === "followingList" && isFollowingListPage() && isRect(data.followingListCard) && isRect(data.followingListThumb)) {
moduleReady.followingList = [
setVar("--tm-thumb-following-list-width", data.followingListCard.width),
setCardHeightVar("--tm-thumb-following-list-card-height", data.followingListCard.height),
setVar("--tm-thumb-following-list-thumb-width", data.followingListThumb.width),
setVar("--tm-thumb-following-list-thumb-height", data.followingListThumb.height),
].every(Boolean);
} else if (name === "related" && isRect(data.relatedCard) && isRect(data.relatedThumb)) { } else if (name === "related" && isRect(data.relatedCard) && isRect(data.relatedThumb)) {
const widthReady = setVar("--tm-thumb-related-width", data.relatedCard.width); const widthReady = setVar("--tm-thumb-related-width", data.relatedCard.width);
const cardHeightReady = setCardHeightVar("--tm-thumb-related-card-height", data.relatedCard.height); const cardHeightReady = setCardHeightVar("--tm-thumb-related-card-height", data.relatedCard.height);
@@ -628,12 +614,10 @@
*/ */
const followingListPage = isFollowingListPage(); const followingListPage = isFollowingListPage();
const measured = { const measured = {
// 首页普通房间列表:卡片宽高 + 缩略图宽高四项独立探测 // 首页普通房间列表:只探测卡片宽度。缩略图跟随卡片自然缩放,避免重复放大
homeCard: moduleReady.home || followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"), homeCard: moduleReady.home || followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"),
homeThumb: moduleReady.home || followingListPage ? null : firstRect("#roomlist_root .RoomCardThumbnail, #roomlist_root .room_thumbnail_container"), // 关注页房间列表:只探测卡片宽度;独立缓存,避免和首页混用。
// 关注页房间列表:选择器和首页相似,但独立探测、独立缓存、独立 CSS 变量。
followingListCard: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"), followingListCard: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root li.RoomCard, #roomlist_root li.roomCard"),
followingListThumb: moduleReady.followingList || !followingListPage ? null : firstRect("#roomlist_root .RoomCardThumbnail, #roomlist_root .room_thumbnail_container"),
// 频道页“更多这样的房间”:卡片宽高和缩略图宽高都要探测,否则放大后行高容易不齐。 // 频道页“更多这样的房间”:卡片宽高和缩略图宽高都要探测,否则放大后行高容易不齐。
relatedCard: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid > li.RoomCard, #main.roomPage ul.list:has(li.roomCard) li.roomCard, .BaseRoomContents ul.list:has(li.roomCard) li.roomCard"), relatedCard: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid > li.RoomCard, #main.roomPage ul.list:has(li.roomCard) li.roomCard, .BaseRoomContents ul.list:has(li.roomCard) li.roomCard"),
relatedThumb: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail, #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container, .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container"), relatedThumb: moduleReady.related ? null : firstRect(".BaseRoomContents ul.RoomCardGrid .RoomCardThumbnail, #main.roomPage ul.list:has(li.roomCard) .room_thumbnail_container, .BaseRoomContents ul.list:has(li.roomCard) .room_thumbnail_container"),
@@ -655,35 +639,23 @@
}; };
if (!moduleReady.home && !followingListPage) { if (!moduleReady.home && !followingListPage) {
// home 依赖 card/thumb 各自宽高;成功后立刻缓存,后续普通列表页面可直接套用。 // home 依赖卡片宽度;成功后立刻缓存,后续普通列表页面可直接套用。
moduleReady.home = [ moduleReady.home = setVar("--tm-thumb-home-width", measured.homeCard && measured.homeCard.width);
setVar("--tm-thumb-home-width", measured.homeCard && measured.homeCard.width),
setCardHeightVar("--tm-thumb-home-card-height", measured.homeCard && measured.homeCard.height),
setVar("--tm-thumb-home-thumb-width", measured.homeThumb && measured.homeThumb.width),
setVar("--tm-thumb-home-thumb-height", measured.homeThumb && measured.homeThumb.height),
].every(Boolean);
setModuleReady("home", moduleReady.home); setModuleReady("home", moduleReady.home);
if (moduleReady.home) { if (moduleReady.home) {
updateCacheModule("home", { updateCacheModule("home", {
homeCard: cloneRect(measured.homeCard), homeCard: cloneRect(measured.homeCard),
homeThumb: cloneRect(measured.homeThumb),
}); });
} }
} }
if (!moduleReady.followingList && followingListPage) { if (!moduleReady.followingList && followingListPage) {
// followingList 单独缓存,避免关注页拿到首页的原始宽度。 // followingList 单独缓存,避免关注页拿到首页的原始宽度。
moduleReady.followingList = [ moduleReady.followingList = setVar("--tm-thumb-following-list-width", measured.followingListCard && measured.followingListCard.width);
setVar("--tm-thumb-following-list-width", measured.followingListCard && measured.followingListCard.width),
setCardHeightVar("--tm-thumb-following-list-card-height", measured.followingListCard && measured.followingListCard.height),
setVar("--tm-thumb-following-list-thumb-width", measured.followingListThumb && measured.followingListThumb.width),
setVar("--tm-thumb-following-list-thumb-height", measured.followingListThumb && measured.followingListThumb.height),
].every(Boolean);
setModuleReady("followingList", moduleReady.followingList); setModuleReady("followingList", moduleReady.followingList);
if (moduleReady.followingList) { if (moduleReady.followingList) {
updateCacheModule("followingList", { updateCacheModule("followingList", {
followingListCard: cloneRect(measured.followingListCard), followingListCard: cloneRect(measured.followingListCard),
followingListThumb: cloneRect(measured.followingListThumb),
}); });
} }
} }

View File

@@ -89,6 +89,11 @@ assert.match(
/data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-width/, /data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-width/,
"followed-cams list should be separated from homepage list sizing", "followed-cams list should be separated from homepage list sizing",
); );
assert.match(
capturedCss,
/data-tm-thumb-scale-following-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-following-list-width\),\s*1fr\)\)\s*!important/,
"followed-cams list should keep the site's stretching grid alignment while using detected minimum width",
);
assert.match( assert.match(
capturedCss, capturedCss,
/#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*var\(--tm-thumb-related-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/, /#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*var\(--tm-thumb-related-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
@@ -251,8 +256,8 @@ const cacheFakeDocument = {
addEventListener() {}, addEventListener() {},
}; };
const cacheStore = new Map([ const cacheStore = new Map([
["tm-thumb-scale:size-cache:v7", JSON.stringify({ ["tm-thumb-scale:size-cache:v9", JSON.stringify({
schema: 7, schema: 9,
scale: 2, scale: 2,
cardHeightScale: 1.55, cardHeightScale: 1.55,
modules: { modules: {
@@ -345,17 +350,15 @@ vm.runInNewContext(source, {
localStorage: { localStorage: {
getItem() { getItem() {
return JSON.stringify({ return JSON.stringify({
schema: 7, schema: 9,
scale: 2, scale: 2,
cardHeightScale: 1.55, cardHeightScale: 1.55,
modules: { modules: {
home: { home: {
homeCard: { width: 174, height: 120 }, homeCard: { width: 174, height: 120 },
homeThumb: { width: 174, height: 98 },
}, },
followingList: { followingList: {
followingListCard: { width: 190, height: 120 }, followingListCard: { width: 190, height: 120 },
followingListThumb: { width: 188, height: 106 },
}, },
}, },
}); });
@@ -382,7 +385,7 @@ vm.runInNewContext(source, {
assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), "1", "followed-cams should use its own cached list module"); assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), "1", "followed-cams should use its own cached list module");
assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply homepage cache"); assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply homepage cache");
assert.equal(followingVars.get("--tm-thumb-following-list-width"), "380px"); assert.equal(followingVars.get("--tm-thumb-following-list-width"), "380px");
assert.equal(followingVars.get("--tm-thumb-following-list-card-height"), "186px"); assert.equal(followingVars.get("--tm-thumb-following-list-card-height"), undefined);
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-width"), "376px"); assert.equal(followingVars.get("--tm-thumb-following-list-thumb-width"), undefined);
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-height"), "212px"); assert.equal(followingVars.get("--tm-thumb-following-list-thumb-height"), undefined);
assert.equal(followingVars.get("--tm-thumb-home-width"), undefined); assert.equal(followingVars.get("--tm-thumb-home-width"), undefined);