Sleazy Fork is available in English.
Enhances avbase.net: Unified media gallery, full width layout, fixed 1-row actresses, forced 6 works per row default, copy ID + Jable/MissAV search buttons.
// ==UserScript==
// @name AvBase Ultimate Enhancer
// @namespace http://tampermonkey.net/
// @version 3.2.2
// @description Enhances avbase.net: Unified media gallery, full width layout, fixed 1-row actresses, forced 6 works per row default, copy ID + Jable/MissAV search buttons.
// @match https://www.avbase.net/*
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const DEFAULT_WORKS_PER_ROW = 6;
const WORKS_PER_ROW_KEY = 'avbase-works-per-row-v2';
const VOLUME_PREF_KEY = 'avbase-volume-preference';
const RESULT_CARD_SELECTOR = '.bg-base.border.border-light.rounded-lg.overflow-hidden';
const getStoredVolume = () => parseFloat(localStorage.getItem(VOLUME_PREF_KEY)) || 1.0;
const setStoredVolume = (vol) => localStorage.setItem(VOLUME_PREF_KEY, vol.toString());
const isDetailPage = () => {
const path = window.location.pathname;
if (/^\/works\/(date|recent|popular|ranking)/i.test(path)) return false;
return /^\/works\/[^/]+\/?$/.test(path);
};
GM_addStyle(`
:root { --avbase-works-per-row: 6; }
main .container.max-w-7xl, .cl-container {
max-width: 98% !important;
width: 100% !important;
padding-left: 1% !important;
padding-right: 1% !important;
margin: 0 auto !important;
box-sizing: border-box !important;
}
.actress-flex-row {
display: flex !important;
flex-wrap: nowrap !important;
gap: 10px !important;
width: 100% !important;
overflow: hidden !important;
}
.actress-flex-item {
flex: 1 1 0 !important;
min-width: 0 !important;
max-width: none !important;
width: auto !important;
}
.actress-flex-item .flex-1 {
min-width: 0 !important;
overflow: hidden;
}
.actress-flex-item .font-semibold, .actress-flex-item a {
white-space: nowrap !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
display: block;
}
.avbase-detail-section {
grid-template-columns: minmax(0, 1fr) 350px !important;
max-width: 98% !important;
margin: 0 auto;
width: 100%;
}
@media (max-width: 1023px) {
.avbase-detail-section { grid-template-columns: 1fr !important; }
.avbase-detail-section > * { grid-column: 1 / -1 !important; }
}
.avbase-detail-section > .md\\:col-start-2.md\\:col-end-4 { grid-column: 1 / 3 !important; }
.avbase-detail-section > .md\\:col-start-2.md\\:col-end-3 { grid-column: 1 / 2 !important; }
.avbase-detail-section > .md\\:col-start-3 { grid-column: 2 / 3 !important; }
.avbase-detail-cover {
height: auto !important;
min-height: 350px;
max-height: 75vh;
aspect-ratio: auto;
}
.avbase-detail-cover img {
object-fit: contain;
width: 100%;
height: 100%;
}
.adaptive-container {
height: auto !important;
width: 100%;
display: flex;
flex-direction: column;
overflow: visible;
padding: 10px 0;
}
.vertical-container {
display: flex;
flex-direction: column;
align-items: flex-start;
width: 100%;
overflow: visible;
height: auto;
gap: 15px;
}
.table-wrapper {
width: 100%;
padding: 0 15px 15px 15px;
height: auto;
flex-shrink: 0;
box-sizing: border-box;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.95);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
transition: opacity 0.3s ease;
backdrop-filter: blur(5px);
}
.overlay.show { opacity: 1; }
.overlay-main-content {
flex: 1;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding: 20px;
box-sizing: border-box;
overflow: hidden;
}
.overlay-image-container {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.overlay-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 4px;
transition: opacity 0.2s ease;
cursor: zoom-out;
}
.video-overlay-wrapper {
width: 100%;
max-width: 900px;
display: flex;
flex-direction: column;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
border-radius: 8px;
overflow: hidden;
background: #000;
}
.video-overlay-container {
width: 100%;
aspect-ratio: 16/9;
position: relative;
background: #000;
}
.vr-hint {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.7);
color: white;
padding: 8px 16px;
border-radius: 20px;
font-size: 14px;
pointer-events: none;
opacity: 0;
transition: opacity 0.5s;
z-index: 10;
backdrop-filter: blur(4px);
border: 1px solid rgba(255,255,255,0.1);
}
.vr-hint.show { opacity: 1; animation: fadeOut 3s forwards 2s; }
@keyframes fadeOut { to { opacity: 0; } }
.overlay-nav-btn {
position: absolute;
top: 50%;
transform: translateY(-50%);
background: rgba(0,0,0,0.5);
color: white;
border: none;
font-size: 2rem;
width: 50px;
height: 50px;
cursor: pointer;
z-index: 1010;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s, transform 0.2s;
}
.overlay-nav-btn:hover {
background: rgba(0,0,0,0.8);
transform: translateY(-50%) scale(1.1);
}
.overlay-prev { left: 20px; }
.overlay-next { right: 20px; }
.overlay-close-btn {
position: absolute;
top: 20px;
right: 20px;
background: rgba(0,0,0,0.5);
color: white;
border: none;
font-size: 1.5rem;
width: 44px;
height: 44px;
border-radius: 50%;
cursor: pointer;
z-index: 1010;
display: flex;
justify-content: center;
align-items: center;
transition: background 0.2s, transform 0.2s;
}
.overlay-close-btn:hover {
background: rgba(239, 68, 68, 0.9);
transform: rotate(90deg);
}
.overlay-thumbnails-wrapper {
width: 100%;
height: 100px;
background: rgba(0,0,0,0.7);
display: flex;
align-items: center;
padding: 10px;
box-sizing: border-box;
overflow-x: auto;
scroll-behavior: smooth;
-ms-overflow-style: none;
scrollbar-width: none;
border-top: 1px solid rgba(255,255,255,0.1);
}
.overlay-thumbnails-wrapper::-webkit-scrollbar { display: none; }
.overlay-thumb-container {
position: relative;
height: 100%;
display: inline-block;
margin-right: 10px;
cursor: pointer;
flex-shrink: 0;
border-radius: 4px;
border: 2px solid transparent;
transition: transform 0.2s, border 0.2s;
}
.overlay-thumb-container:hover { transform: scale(1.03); }
.overlay-thumb-container.active {
border: 2px solid #60a5fa;
transform: scale(1.05);
z-index: 2;
}
.overlay-thumb {
height: 100%;
max-width: 140px;
object-fit: cover;
border-radius: 2px;
opacity: 0.4;
transition: opacity 0.2s;
display: block;
}
.overlay-thumb-container:hover .overlay-thumb,
.overlay-thumb-container.active .overlay-thumb { opacity: 1; }
.overlay-thumb-play {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.7);
border-radius: 50%;
width: 34px;
height: 34px;
display: flex;
justify-content: center;
align-items: center;
pointer-events: none;
border: 1px solid rgba(255,255,255,0.2);
}
.overlay-thumb-play svg {
width: 18px;
height: 18px;
fill: white;
margin-left: 2px;
}
.controls-bar {
width: 100%;
background-color: rgba(0,0,0,0.8);
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
box-sizing: border-box;
border-radius: 0 0 8px 8px;
border-top: 1px solid rgba(255,255,255,0.1);
}
.controls-bar button {
background: transparent;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
padding: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
margin-right: 8px;
}
.controls-bar button:hover { background: rgba(255,255,255,0.1); }
.controls-bar button img {
width: 22px;
height: 22px;
filter: invert(1);
}
.controls-bar button:last-child { margin-right: 0; }
.zoom-slider-container, .volume-slider-container {
display: flex;
align-items: center;
position: relative;
margin: 0;
}
.zoom-slider, .volume-slider {
-webkit-appearance: none;
appearance: none;
width: 0;
height: 4px;
background: #4b5563;
outline: none;
opacity: 0;
transition: opacity 0.3s, width 0.3s;
border-radius: 4px;
margin: 0 5px;
vertical-align: middle;
}
.zoom-slider.show, .volume-slider.show {
opacity: 1;
width: 80px;
}
.volume-slider:hover, .zoom-slider:hover { opacity: 1; }
.zoom-slider::-webkit-slider-thumb, .volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
background: #fff;
cursor: pointer;
border-radius: 50%;
box-shadow: 0 0 4px rgba(0,0,0,0.5);
}
.zoom-value {
color: #d1d5db;
font-size: 13px;
margin-right: 8px;
width: 30px;
text-align: right;
opacity: 0;
transition: opacity 0.3s;
}
.zoom-value.show { opacity: 1; }
.progress-container {
flex: 1;
display: flex;
align-items: center;
margin: 0 15px;
position: relative;
cursor: pointer;
height: 20px;
}
.progress-bar-bg {
width: 100%;
height: 6px;
background: #374151;
border-radius: 3px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: #ef4444;
width: 0%;
transition: width 0.1s linear;
}
.time-display {
color: #d1d5db;
font-size: 13px;
margin-left: 10px;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.scene-thumbnails-row {
display: flex;
gap: 10px;
overflow-x: auto;
width: 100%;
padding: 0 15px;
box-sizing: border-box;
scroll-behavior: smooth;
-ms-overflow-style: none;
scrollbar-width: none;
}
.scene-thumbnails-row::-webkit-scrollbar { display: none; }
.scene-thumbnail {
height: 130px;
width: auto;
cursor: pointer;
border-radius: 6px;
flex-shrink: 0;
transition: transform 0.2s, opacity 0.2s;
opacity: 0.85;
object-fit: cover;
border: 1px solid rgba(255,255,255,0.1);
}
.scene-thumbnail:hover {
transform: scale(1.05);
opacity: 1;
border-color: rgba(255,255,255,0.3);
}
.play-trailer-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(239, 68, 68, 0.9);
border: none;
border-radius: 50%;
width: 70px;
height: 70px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
transition: transform 0.2s, background 0.2s;
z-index: 10;
backdrop-filter: blur(2px);
}
.play-trailer-btn:hover {
transform: translate(-50%, -50%) scale(1.1);
background: rgba(220, 38, 38, 1);
}
.play-trailer-btn svg {
width: 35px;
height: 35px;
fill: white;
margin-left: 5px;
}
a-scene .a-canvas + div { display: none !important; }
.sm\\:grid-cols-2, .sm\\:grid-cols-3, .md\\:grid-cols-3, .md\\:grid-cols-4, .lg\\:grid-cols-4, .lg\\:grid-cols-5 {
grid-template-columns: repeat(var(--avbase-works-per-row, 6), minmax(0, 1fr));
}
.avbase-pinterest-grid {
display: grid !important;
align-items: start !important;
gap: 16px !important;
grid-template-columns: repeat(var(--avbase-works-per-row, 6), minmax(0, 1fr)) !important;
}
.avbase-pinterest-grid > * {
width: 100% !important;
min-width: 0 !important;
margin: 0 !important;
}
.avbase-pinterest-grid .bg-base.border.border-light.rounded-lg.overflow-hidden {
border-radius: 12px !important;
overflow: hidden !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.05) !important;
transition: transform 0.2s, box-shadow 0.2s;
height: 100% !important;
}
.avbase-pinterest-grid .bg-base.border.border-light.rounded-lg.overflow-hidden:hover {
transform: translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.large-image {
width: 100%;
height: auto;
max-height: 280px;
object-fit: contain;
display: block;
border-radius: 4px;
cursor: zoom-in;
background-color: transparent;
}
.title-below-image {
font-size: 0.75rem;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
padding: 8px;
text-align: left;
color: #d1d5db;
text-decoration: none;
font-weight: 500;
width: 100%;
box-sizing: border-box;
}
.avbase-compact-tags-bar { width: 100%; }
.avbase-sidebar-card {
display: flex !important;
flex-wrap: wrap !important;
align-items: center !important;
gap: 10px !important;
padding: 12px 16px !important;
border-radius: 12px !important;
}
.avbase-sidebar-card > div:first-child {
display: flex !important;
align-items: center !important;
gap: 10px !important;
flex: 1 1 auto;
min-width: 0;
}
.avbase-sidebar-card > div:first-child > div:first-child {
display: flex !important;
align-items: center !important;
gap: 6px !important;
flex-wrap: wrap !important;
}
.avbase-sidebar-card button { margin-top: 0 !important; }
.avbase-pinterest-grid .flex.flex-wrap.gap-2 {
flex-wrap: nowrap !important;
overflow-x: auto !important;
overflow-y: hidden !important;
padding-bottom: 8px !important;
margin-bottom: -4px !important;
-ms-overflow-style: none;
scrollbar-width: none;
}
.avbase-pinterest-grid .flex.flex-wrap.gap-2::-webkit-scrollbar { display: none; }
.avbase-pinterest-grid .chip.chip-sm {
flex-shrink: 0 !important;
height: 20px !important;
font-size: 0.7rem !important;
padding: 0 6px !important;
background-color: rgba(255, 255, 255, 0.08) !important;
}
.avbase-works-toolbar {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
flex-wrap: wrap;
}
.avbase-works-toolbar label {
color: #94a3b8;
font-size: 12px;
white-space: nowrap;
}
.avbase-works-toolbar select {
height: 32px;
min-width: 84px;
padding: 0 10px;
color: #e5e7eb;
background: #111827;
border: 1px solid #374151;
border-radius: 8px;
outline: none;
cursor: pointer;
font-size: 13px;
}
.jable-btn, .missav-btn, .copy-btn {
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
height: 2rem !important;
min-height: 2rem !important;
line-height: 2rem !important;
text-align: center !important;
background-color: #4b5563 !important;
color: white !important;
border: none !important;
border-radius: 0.375rem !important;
font-size: 0.75rem !important;
font-weight: 500 !important;
padding: 0 0.7rem !important;
cursor: pointer !important;
white-space: nowrap !important;
user-select: none !important;
text-decoration: none !important;
transition: background-color 0.15s ease, opacity 0.15s ease !important;
margin-left: 0.35rem !important;
flex: 0 0 auto !important;
}
.jable-btn:hover, .missav-btn:hover, .copy-btn:hover {
background-color: #374151 !important;
}
.copy-btn {
max-width: none !important;
min-width: 5rem !important;
overflow: hidden !important;
text-overflow: ellipsis !important;
white-space: nowrap !important;
}
.transparent { background-color: transparent !important; }
.sukebei-table {
width: 100%;
border-collapse: collapse;
font-size: 13px;
margin: 0;
color: inherit;
}
.sukebei-table th, .sukebei-table td {
padding: 10px;
border-bottom: 1px solid var(--fallback-bc, rgba(255, 255, 255, 0.1));
}
.sukebei-table tr:hover { background-color: rgba(255, 255, 255, 0.05); }
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
color: #9ca3af;
font-size: 14px;
}
@media (max-width: 1023px) {
.avbase-sidebar-card { padding: 10px 12px !important; }
}
@media (max-width: 639px) {
.avbase-works-toolbar {
width: 100%;
justify-content: flex-end;
margin-top: 8px;
}
}
`);
function loadAFrame() {
if (!document.querySelector('script[src*="aframe.min.js"]')) {
const script = document.createElement('script');
script.src = 'https://aframe.io/releases/1.7.0/aframe.min.js';
document.head.appendChild(script);
}
}
function formatTime(seconds) {
if (isNaN(seconds)) return '0:00';
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${s.toString().padStart(2, '0')}`;
}
function processDmmLink(link) {
try {
if (link.includes('al.dmm.co.jp')) {
return decodeURIComponent(new URLSearchParams(new URL(link).search).get('lurl') || link);
}
return link;
} catch (e) {
return link;
}
}
function checkImageExists(url) {
return new Promise(r => {
const img = new Image();
img.onload = () => r(true);
img.onerror = () => r(false);
img.src = url;
setTimeout(() => r(false), 3000);
});
}
async function checkVideoAvailability(url) {
try {
const r = await fetch(url, { method: 'HEAD' });
return r.ok;
} catch (e) {
return false;
}
}
function getLargerImageUrl(sUrl) {
let m = sUrl.match(/\/([^/]+)(ps|jm)\.jpg$/i);
if (!m) return null;
let v = m[1];
const s = m[2].toLowerCase();
if (s === 'ps' && sUrl.includes('awsimgsrc')) {
if (/^[a-zA-Z]+[0-9]{3}$/.test(v)) v = v.replace(/([a-zA-Z]+)([0-9]{3})/, '$100$2');
return `https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/${v}/${v}pl.jpg`;
}
if (s === 'jm' && sUrl.includes('pics')) return sUrl.replace(/jm\.jpg$/i, 'jp.jpg');
return sUrl.replace(/ps\.jpg$/i, 'pl.jpg');
}
function fetchSukebeiResults(videoId) {
return new Promise((res, rej) => {
GM_xmlhttpRequest({
method: 'GET',
url: `https://sukebei.nyaa.si/?page=rss&q=${encodeURIComponent(videoId)}`,
onload: r => {
if (r.status === 200) {
const xml = new DOMParser().parseFromString(r.responseText, 'text/xml');
res(Array.from(xml.getElementsByTagName('item')).map(i => ({
name: i.getElementsByTagName('title')[0]?.textContent.trim(),
link: i.getElementsByTagName('link')[0]?.textContent.trim(),
size: i.getElementsByTagName('nyaa:size')[0]?.textContent.trim(),
date: i.getElementsByTagName('pubDate')[0]?.textContent.trim(),
magnet: 'magnet:?xt=urn:btih:' + (i.getElementsByTagName('nyaa:infoHash')[0]?.textContent.trim() || '')
})));
} else {
rej(r.status);
}
},
onerror: rej
});
});
}
function getStoredWorksPerRow() {
const p = parseInt(localStorage.getItem(WORKS_PER_ROW_KEY), 10);
return (Number.isFinite(p) && p >= 3 && p <= 10) ? p : DEFAULT_WORKS_PER_ROW;
}
function setStoredWorksPerRow(v) {
const c = Math.max(3, Math.min(10, parseInt(v, 10) || DEFAULT_WORKS_PER_ROW));
localStorage.setItem(WORKS_PER_ROW_KEY, String(c));
return c;
}
function getEffectiveWorksPerRow() {
const p = getStoredWorksPerRow();
if (window.innerWidth < 640) return Math.min(p, 2);
if (window.innerWidth < 1024) return Math.min(p, 3);
return p;
}
function updateWorksPerRowVariable() {
document.documentElement.style.setProperty('--avbase-works-per-row', String(getEffectiveWorksPerRow()));
}
function getWorksGrid() {
const firstCard = document.querySelector(RESULT_CARD_SELECTOR);
if (!firstCard) return null;
let current = firstCard.parentElement;
while (current && current !== document.body) {
if (current.querySelectorAll(RESULT_CARD_SELECTOR).length >= 2 && current.className.includes('grid')) return current;
current = current.parentElement;
}
return null;
}
function compactSidebarIntoTopBar() {
const main = document.querySelector('.lg\\:col-span-3');
const sidebar = document.querySelector('.lg\\:col-span-1');
const layoutGrid = document.querySelector('.grid.grid-cols-1.lg\\:grid-cols-4');
if (!main || !sidebar || !layoutGrid) return;
Object.assign(layoutGrid.style, { gridTemplateColumns: 'minmax(0, 1fr)', gap: '16px' });
Object.assign(main.style, { gridColumn: '1 / -1', width: '100%' });
let compactBar = main.querySelector('.avbase-compact-tags-bar');
if (!compactBar) {
compactBar = document.createElement('div');
compactBar.className = 'avbase-compact-tags-bar';
main.insertBefore(compactBar, main.firstChild);
}
const sidePanel = sidebar.querySelector('.flex-1.flex.flex-col, .flex.flex-col.gap-4');
if (sidePanel && sidePanel.parentElement !== compactBar) {
sidePanel.classList.add('avbase-sidebar-card');
const btn = sidePanel.querySelector('button.w-full');
if (btn) Object.assign(btn.style, { width: 'auto', marginTop: '0', flex: '0 0 auto' });
compactBar.appendChild(sidePanel);
sidebar.style.display = 'none';
}
}
function ensureWorksPerRowControl() {
const title = Array.from(document.querySelectorAll('h3, span.rounded-full, h2')).find(el =>
(el.textContent.includes('作品') || el.textContent.includes('Today') || el.textContent.includes('Recently')) ||
el.closest('.flex.items-center.justify-start.gap-2')
);
const headerRow = title?.closest('.flex.items-center.justify-start.gap-2') || title?.parentElement;
if (!headerRow || headerRow.tagName === 'SECTION') return;
Object.assign(headerRow.style, {
justifyContent: 'space-between',
alignItems: 'center',
flexWrap: 'wrap',
display: 'flex'
});
if (!headerRow.querySelector('.avbase-works-toolbar')) {
const toolbar = document.createElement('div');
toolbar.className = 'avbase-works-toolbar';
toolbar.innerHTML = `<label>每行顯示</label><select class="avbase-works-per-row-select">${[3,4,5,6,7,8,9,10].map(n => `<option value="${n}">${n} 個</option>`).join('')}</select>`;
const select = toolbar.querySelector('select');
select.value = String(getStoredWorksPerRow());
select.onchange = () => {
setStoredWorksPerRow(select.value);
applyPinterestLayout();
};
headerRow.appendChild(toolbar);
}
}
function enhanceActressesGrid() {
document.querySelectorAll('h2').forEach(h2 => {
const titleText = h2.textContent.trim();
if (titleText.includes('Featured Actresses') || titleText.includes('New Actresses')) {
const grid = h2.nextElementSibling;
if (grid && grid.classList.contains('grid') && !grid.dataset.actressEnhanced) {
grid.dataset.actressEnhanced = 'true';
grid.className = 'actress-flex-row';
Array.from(grid.children).forEach(child => {
child.className = 'actress-flex-item ' + child.className.replace(/w-full/g, '');
});
}
}
});
}
function applyPinterestLayout() {
if (isDetailPage()) return;
updateWorksPerRowVariable();
compactSidebarIntoTopBar();
ensureWorksPerRowControl();
enhanceActressesGrid();
const grid = getWorksGrid();
if (grid) {
grid.classList.add('avbase-pinterest-grid');
Object.assign(grid.style, {
display: 'grid',
gap: '16px',
alignItems: 'start',
gridTemplateColumns: `repeat(${getEffectiveWorksPerRow()}, minmax(0, 1fr))`
});
}
}
function buildUnifiedGalleryOverlay(mediaArray, startIndex) {
let currentIndex = startIndex;
const overlay = document.createElement('div');
overlay.className = 'overlay';
const mainContent = document.createElement('div');
mainContent.className = 'overlay-main-content';
const prevBtn = document.createElement('button');
prevBtn.className = 'overlay-nav-btn overlay-prev';
prevBtn.innerHTML = '❮';
const nextBtn = document.createElement('button');
nextBtn.className = 'overlay-nav-btn overlay-next';
nextBtn.innerHTML = '❯';
const closeBtn = document.createElement('button');
closeBtn.className = 'overlay-close-btn';
closeBtn.innerHTML = '✕';
const thumbWrapper = document.createElement('div');
thumbWrapper.className = 'overlay-thumbnails-wrapper';
const thumbElements = [];
mediaArray.forEach((item, idx) => {
const thumbDiv = document.createElement('div');
thumbDiv.className = 'overlay-thumb-container';
const thumb = document.createElement('img');
thumb.className = 'overlay-thumb';
thumb.src = item.thumbSrc;
thumbDiv.appendChild(thumb);
if (item.type === 'video') {
const playIcon = document.createElement('div');
playIcon.className = 'overlay-thumb-play';
playIcon.innerHTML = `<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`;
thumbDiv.appendChild(playIcon);
}
thumbDiv.onclick = (e) => {
e.stopPropagation();
updateGallery(idx);
};
thumbElements.push(thumbDiv);
thumbWrapper.appendChild(thumbDiv);
});
overlay.append(closeBtn, prevBtn, nextBtn, mainContent, thumbWrapper);
document.body.appendChild(overlay);
let globalVideoInterval;
const updateGallery = async (index) => {
const oldVid = mainContent.querySelector('video');
if (oldVid) oldVid.pause();
const oldVr = mainContent.querySelector('a-videosphere')?.components?.material?.material?.map?.image;
if (oldVr) oldVr.pause();
mainContent.innerHTML = '';
clearInterval(globalVideoInterval);
currentIndex = index;
thumbElements.forEach((th, i) => {
if (i === currentIndex) {
th.classList.add('active');
th.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
} else {
th.classList.remove('active');
}
});
const item = mediaArray[currentIndex];
if (item.type === 'image') {
const imgContainer = document.createElement('div');
imgContainer.className = 'overlay-image-container';
const img = document.createElement('img');
img.className = 'overlay-image';
img.style.opacity = '0.3';
imgContainer.appendChild(img);
mainContent.appendChild(imgContainer);
img.src = typeof item.getLargeUrl === 'function' ? await item.getLargeUrl() : item.largeUrl;
img.onload = () => img.style.opacity = '1';
imgContainer.onclick = closeOverlay;
img.onclick = closeOverlay;
} else if (item.type === 'video') {
const wrapper = document.createElement('div');
wrapper.className = 'video-overlay-wrapper';
const container = document.createElement('div');
container.className = 'video-overlay-container';
const hint = document.createElement('div');
hint.className = 'vr-hint';
hint.textContent = '🖱️ 滑鼠拖曳可環顧四周';
if (item.isVR) container.appendChild(hint);
wrapper.appendChild(container);
mainContent.appendChild(wrapper);
let cameraZoom = 160;
let isVRMode = item.isVR;
const getCurrentVideo = () =>
isVRMode
? container.querySelector('a-videosphere')?.components?.material?.material?.map?.image
: container.querySelector('video');
const initPlayer = () => {
container.innerHTML = '';
if (isVRMode) container.appendChild(hint);
if (isVRMode) {
const scene = document.createElement('a-scene');
scene.setAttribute('embedded', '');
scene.setAttribute('vr-mode-ui', 'enabled: false');
scene.innerHTML = `
<a-videosphere src="${item.videoUrl}" rotation="0 -180 0" phi-start="0" phi-length="180" autoplay crossorigin="anonymous"></a-videosphere>
<a-camera id="vr-cam" position="0 0 ${cameraZoom}" rotation="0 90 0" look-controls="reverseMouseDrag: true"></a-camera>
`;
container.appendChild(scene);
setTimeout(() => hint.classList.add('show'), 500);
} else {
const vid = document.createElement('video');
vid.src = item.videoUrl;
vid.autoplay = true;
vid.playsInline = true;
Object.assign(vid.style, { width: '100%', height: '100%', objectFit: 'contain' });
vid.volume = getStoredVolume();
container.appendChild(vid);
}
};
initPlayer();
const controlsBar = document.createElement('div');
controlsBar.className = 'controls-bar';
const leftControls = document.createElement('div');
leftControls.style.display = 'flex';
leftControls.style.alignItems = 'center';
const rightControls = document.createElement('div');
rightControls.style.display = 'flex';
rightControls.style.alignItems = 'center';
const createBtn = (icon, fn, title = '') => {
const b = document.createElement('button');
b.title = title;
b.innerHTML = `<img src="${icon}">`;
b.onclick = fn;
return b;
};
const playBtn = createBtn(
'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/play.svg',
() => {
const v = getCurrentVideo();
if (!v) return;
v.paused ? v.play() : v.pause();
playBtn.firstChild.src = v.paused
? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/play.svg'
: 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/pause.svg';
}
);
leftControls.appendChild(playBtn);
const progressContainer = document.createElement('div');
progressContainer.className = 'progress-container';
const progressBg = document.createElement('div');
progressBg.className = 'progress-bar-bg';
const progressFill = document.createElement('div');
progressFill.className = 'progress-bar-fill';
progressBg.appendChild(progressFill);
progressContainer.appendChild(progressBg);
const timeDisplay = document.createElement('div');
timeDisplay.className = 'time-display';
timeDisplay.textContent = '0:00 / 0:00';
progressContainer.onclick = (e) => {
const v = getCurrentVideo();
if (!v || !v.duration) return;
const rect = progressContainer.getBoundingClientRect();
v.currentTime = ((e.clientX - rect.left) / rect.width) * v.duration;
};
const volContainer = document.createElement('div');
volContainer.className = 'volume-slider-container';
const volBtn = createBtn(
'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg',
() => {
const v = getCurrentVideo();
if (!v) return;
v.muted = !v.muted;
volBtn.firstChild.src = v.muted
? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/mute.svg'
: 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg';
}
);
const volSlider = document.createElement('input');
Object.assign(volSlider, {
type: 'range',
className: 'volume-slider',
min: '0',
max: '1',
step: '0.01',
value: getStoredVolume()
});
volSlider.oninput = () => {
const v = getCurrentVideo();
if (!v) return;
v.muted = false;
v.volume = parseFloat(volSlider.value);
setStoredVolume(v.volume);
volBtn.firstChild.src = v.volume === 0
? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/mute.svg'
: 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg';
};
volContainer.append(volBtn, volSlider);
volContainer.onmouseenter = () => volSlider.classList.add('show');
volContainer.onmouseleave = () => volSlider.classList.remove('show');
const vrControls = document.createElement('div');
vrControls.style.display = 'flex';
vrControls.style.alignItems = 'center';
const zoomContainer = document.createElement('div');
zoomContainer.className = 'zoom-slider-container';
const zoomBtn = createBtn(
'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/search.svg',
() => {
zoomSlider.classList.toggle('show');
zoomValue.classList.toggle('show');
}
);
const zoomSlider = document.createElement('input');
Object.assign(zoomSlider, {
type: 'range',
className: 'zoom-slider',
min: '-480',
max: '480',
step: '10',
value: '160'
});
const zoomValue = document.createElement('span');
zoomValue.className = 'zoom-value';
zoomValue.textContent = '160';
zoomSlider.oninput = () => {
if (!isVRMode) return;
cameraZoom = parseFloat(zoomSlider.value);
zoomValue.textContent = cameraZoom;
container.querySelector('#vr-cam')?.setAttribute('position', `0 0 ${cameraZoom}`);
};
zoomContainer.append(zoomValue, zoomSlider, zoomBtn);
zoomContainer.style.display = isVRMode ? 'flex' : 'none';
vrControls.append(zoomContainer);
const toggleVRBtn = createBtn(
isVRMode
? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/2d-label-icon.svg'
: 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/vr-label-icon.svg',
function() {
const v = getCurrentVideo();
const curTime = v ? v.currentTime : 0;
const wasPaused = v ? v.paused : false;
isVRMode = !isVRMode;
initPlayer();
setTimeout(() => {
const newV = getCurrentVideo();
if (newV) {
newV.currentTime = curTime;
if (!wasPaused) newV.play();
}
this.firstChild.src = isVRMode
? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/2d-label-icon.svg'
: 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/vr-label-icon.svg';
zoomContainer.style.display = isVRMode ? 'flex' : 'none';
}, 500);
}
);
const fsBtn = createBtn(
'https://raw.githubusercontent.com/leogfa/svg/65fdb85e8047e4b5b6e221e5516962c534d8efb6/fullscreen.svg',
() => document.fullscreenElement ? document.exitFullscreen() : wrapper.requestFullscreen()
);
rightControls.append(volContainer, vrControls, toggleVRBtn, fsBtn);
controlsBar.append(leftControls, progressContainer, timeDisplay, rightControls);
wrapper.appendChild(controlsBar);
globalVideoInterval = setInterval(() => {
const v = getCurrentVideo();
if (v) {
if (!v.paused && playBtn.firstChild.src.includes('play.svg')) {
playBtn.firstChild.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/pause.svg';
}
if (v.duration) {
progressFill.style.width = `${(v.currentTime / v.duration) * 100}%`;
timeDisplay.textContent = `${formatTime(v.currentTime)} / ${formatTime(v.duration)}`;
}
}
}, 200);
}
prevBtn.style.display = currentIndex === 0 ? 'none' : 'flex';
nextBtn.style.display = currentIndex === mediaArray.length - 1 ? 'none' : 'flex';
};
updateGallery(currentIndex);
setTimeout(() => overlay.classList.add('show'), 10);
const goPrev = (e) => {
if (e) e.stopPropagation();
if (currentIndex > 0) updateGallery(currentIndex - 1);
};
const goNext = (e) => {
if (e) e.stopPropagation();
if (currentIndex < mediaArray.length - 1) updateGallery(currentIndex + 1);
};
const closeOverlay = (e) => {
if (e && e.target !== overlay && e.target !== mainContent && !e.target.classList.contains('overlay-image-container') && e.target !== closeBtn) return;
const oldVid = mainContent.querySelector('video');
if (oldVid) oldVid.pause();
const oldVr = mainContent.querySelector('a-videosphere')?.components?.material?.material?.map?.image;
if (oldVr) oldVr.pause();
clearInterval(globalVideoInterval);
overlay.classList.remove('show');
setTimeout(() => overlay.remove(), 300);
document.removeEventListener('keydown', keyHandler);
};
const keyHandler = (e) => {
if (e.key === 'Escape') closeOverlay();
else if (e.key === 'ArrowLeft') goPrev();
else if (e.key === 'ArrowRight') goNext();
if (mediaArray[currentIndex].type === 'video' && e.code === 'Space') {
const v = mainContent.querySelector('video') || mainContent.querySelector('a-videosphere')?.components?.material?.material?.map?.image;
if (v) {
e.preventDefault();
v.paused ? v.play() : v.pause();
}
}
};
prevBtn.onclick = goPrev;
nextBtn.onclick = goNext;
closeBtn.onclick = closeOverlay;
overlay.onclick = closeOverlay;
document.addEventListener('keydown', keyHandler);
}
async function enhanceDetails() {
if (!isDetailPage() || document.querySelector('.vertical-container')) return;
loadAFrame();
const detailSection = document.querySelector('section.grid[class*="md:grid-cols-[1fr_"]');
if (detailSection) detailSection.classList.add('avbase-detail-section');
const coverContainer = document.querySelector('.h-72.max-w-full.bg-base-300');
if (coverContainer) {
coverContainer.classList.remove('h-72');
coverContainer.classList.add('avbase-detail-cover');
}
const container = document.querySelector('.flex.overflow-x-auto.overflow-y-hidden');
if (!container) return;
const codeElement = document.querySelector('span[dir="rtl"].pl-1.whitespace-nowrap.overflow-hidden.text-ellipsis');
const code = codeElement?.textContent.trim();
const mergeRowLabel = Array.from(document.querySelectorAll('span.text-xs.flex'))
.find(el => el.textContent.includes('名寄せID'));
let mergeRowGrid = null;
let actionContainer = null;
if (mergeRowLabel) {
mergeRowGrid = mergeRowLabel.closest('.grid');
actionContainer =
mergeRowGrid?.querySelector('div.flex.gap-2.items-center') ||
mergeRowGrid?.querySelector('div.flex.gap-2') ||
mergeRowGrid?.lastElementChild;
}
if (actionContainer && !actionContainer.querySelector('.copy-btn')) {
const rawId =
mergeRowGrid?.querySelector('span.text-xs.flex div span:not(.text-gray-400)')?.textContent?.trim()
|| code
|| '';
const videoId = rawId.replace(/^.*:/, '').trim();
if (videoId) {
const encodedId = encodeURIComponent(videoId);
const createBtn = (tag, cls, text, href = '') => {
const el = document.createElement(tag);
el.className = cls;
el.textContent = text;
if (tag === 'a' && href) {
el.href = href;
el.target = '_blank';
el.rel = 'noopener noreferrer';
}
if (tag === 'button') {
el.type = 'button';
}
return el;
};
const copyBtn = createBtn('button', 'copy-btn', videoId);
copyBtn.title = '點擊複製作品ID';
copyBtn.onclick = () => {
navigator.clipboard.writeText(videoId).then(() => {
const oldText = copyBtn.textContent;
copyBtn.textContent = '已複製';
setTimeout(() => { copyBtn.textContent = oldText; }, 1200);
});
};
const jableBtn = createBtn('a', 'jable-btn', 'Jable', `https://jable.tv/search/${encodedId}/`);
const missavBtn = createBtn('a', 'missav-btn', 'MissAV', `https://missav.ai/search/${encodedId}/`);
actionContainer.insertBefore(copyBtn, actionContainer.firstChild);
actionContainer.insertBefore(jableBtn, actionContainer.firstChild.nextSibling);
actionContainer.insertBefore(missavBtn, actionContainer.firstChild.nextSibling.nextSibling);
}
}
const mediaData = [];
let videoIndex = -1;
let coverIndex = -1;
const coverLink = coverContainer?.parentElement;
const coverImg = coverContainer?.querySelector('img');
const coverThumbUrl = coverImg ? coverImg.src : null;
if (coverLink && code) {
let videoUrls = [];
const isVR = /vr|aqu|exmo/i.test(code) || code.startsWith('1fsvss');
const prefix = code.includes('_') ? 'h_1' : code.substr(0, 3);
if ((code.startsWith('1f') && !code.startsWith('1fsvss')) || code.startsWith('1m')) {
videoUrls.push(`https://videos.vpdmm.cc/litevideo/freepv/${code.charAt(0)}/${code.substr(0,3)}/${code.substr(0,6)}${code.substr(8,3)}/${code.substr(0,6)}${code.substr(8,3)}4k.mp4`);
}
videoUrls.push(`https://cc3001.dmm.com/${isVR ? 'vrsample' : 'litevideo/freepv'}/${code.charAt(0)}/${prefix}/${code}/${code}${isVR ? 'vrlite' : 'hhb'}.mp4`);
videoUrls.push(`https://cc3001.dmm.com/litevideo/freepv/${code.charAt(0)}/${prefix}/${code}/${code}_dmb_w.mp4`);
const availableVideoUrl = videoUrls[(await Promise.all(videoUrls.map(checkVideoAvailability))).findIndex(r => r)];
if (availableVideoUrl) {
videoIndex = mediaData.length;
mediaData.push({
type: 'video',
videoUrl: availableVideoUrl,
isVR: isVR,
thumbSrc: coverThumbUrl || 'https://via.placeholder.com/150x100?text=Video'
});
coverLink.style.position = 'relative';
const playBtn = document.createElement('button');
playBtn.className = 'play-trailer-btn';
playBtn.innerHTML = `<svg viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg>`;
playBtn.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
buildUnifiedGalleryOverlay(mediaData, videoIndex);
};
coverLink.appendChild(playBtn);
}
}
if (coverImg && coverLink) {
coverIndex = mediaData.length;
const largeUrl = processDmmLink(coverLink.href) || getLargerImageUrl(coverThumbUrl) || coverThumbUrl;
mediaData.push({ type: 'image', thumbSrc: coverThumbUrl, largeUrl: largeUrl });
coverLink.onclick = (e) => {
if (e.target.closest('.play-trailer-btn')) return;
e.preventDefault();
buildUnifiedGalleryOverlay(mediaData, coverIndex);
};
}
const parentContainer = container.closest('.bg-base-300.w-full.flex');
const verticalContainer = document.createElement('div');
verticalContainer.className = 'vertical-container';
if (parentContainer) {
parentContainer.classList.remove('h-44', 'items-center');
parentContainer.classList.add('adaptive-container');
container.parentNode.replaceChild(verticalContainer, container);
}
const sceneThumbsRow = document.createElement('div');
sceneThumbsRow.className = 'scene-thumbnails-row';
const sceneStartIndex = mediaData.length;
container.querySelectorAll('a').forEach((link, idx) => {
const largeUrl = processDmmLink(link.href);
const img = link.querySelector('img');
const thumbUrl = img ? img.src : largeUrl;
mediaData.push({ type: 'image', thumbSrc: thumbUrl, largeUrl: largeUrl });
const thumbEl = document.createElement('img');
thumbEl.className = 'scene-thumbnail';
thumbEl.src = thumbUrl;
thumbEl.onclick = (e) => {
e.preventDefault();
buildUnifiedGalleryOverlay(mediaData, sceneStartIndex + idx);
};
sceneThumbsRow.appendChild(thumbEl);
});
if (mediaData.length > (coverIndex !== -1 ? 1 : 0)) {
verticalContainer.appendChild(sceneThumbsRow);
}
const videoIdForSearch = (
mergeRowGrid?.querySelector('span.text-xs.flex div span:not(.text-gray-400)')?.textContent
|| codeElement?.textContent
|| ''
).trim().replace(/^.*:/, '');
if (videoIdForSearch) {
const tableWrapper = document.createElement('div');
tableWrapper.className = 'table-wrapper';
tableWrapper.innerHTML = `<div class="loading-spinner">正在搜尋 Sukebei 資源...</div>`;
verticalContainer.appendChild(tableWrapper);
fetchSukebeiResults(videoIdForSearch).then(results => {
tableWrapper.innerHTML = '';
const table = document.createElement('table');
table.className = 'sukebei-table';
if (results.length) {
results.forEach(r => {
const tr = document.createElement('tr');
const pad = n => n < 10 ? '0' + n : n;
const d = new Date(r.date);
tr.innerHTML = `
<td style="width: 60%;">${r.name}</td>
<td style="width: 15%; text-align: center;">
<div style="display:inline-flex; gap:8px; align-items:center;">
<a href="${r.link}" target="_blank" rel="noopener noreferrer">
<img src="https://raw.githubusercontent.com/leogfa/svg/fde17c8702542028c186c6fb170a8bc26a6c1be5/download.svg" style="width:24px;height:24px;filter:invert(1);">
</a>
<button class="mag-btn" data-mag="${r.magnet}" style="background:none;border:none;cursor:pointer;">
<img src="https://raw.githubusercontent.com/leogfa/svg/3edada4e4c0a5c1a83938a427459a488d22ec6a4/magnet.svg" style="width:24px;height:24px;filter:invert(1);">
</button>
</div>
</td>
<td style="width: 10%; text-align: center;">${r.size}</td>
<td style="width: 15%; text-align: center;">${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}</td>
`;
table.appendChild(tr);
});
table.querySelectorAll('.mag-btn').forEach(btn => {
btn.onclick = () => navigator.clipboard.writeText(btn.dataset.mag).then(() => {
btn.textContent = '已複製';
setTimeout(() => {
btn.innerHTML = `<img src="https://raw.githubusercontent.com/leogfa/svg/3edada4e4c0a5c1a83938a427459a488d22ec6a4/magnet.svg" style="width:24px;height:24px;filter:invert(1);">`;
}, 1500);
});
});
} else {
table.innerHTML = `<tr><td colspan="4" style="text-align:center;">找不到Seed</td></tr>`;
}
tableWrapper.appendChild(table);
}).catch(() => {
tableWrapper.innerHTML = `<div class="loading-spinner">搜尋失敗或超時</div>`;
});
}
}
function enhanceImages() {
const containers = document.querySelectorAll(`${RESULT_CARD_SELECTOR}:not([data-enhanced="true"])`);
if (!containers.length) return;
containers.forEach(container => {
container.setAttribute('data-enhanced', 'true');
const imageLink = container.querySelector('div.flex.items-center.justify-center.bg-base-2 a[rel="noopener noreferrer sponsored"] img, div.flex.items-center.justify-center.bg-base2 a[rel="noopener noreferrer sponsored"] img');
const titleLink = container.querySelector('a.text-md.font-bold.btn.btn-ghost.rounded-lg.m-1, a[class*="line-clamp"]');
if (imageLink && titleLink) {
const smallUrl = imageLink.src;
let largeUrl = smallUrl;
if (smallUrl.match(/ps\.jpg$/i) && smallUrl.includes('awsimgsrc.dmm.co.jp')) {
let vid = smallUrl.match(/\/([^/]+)ps\.jpg$/i)?.[1];
if (vid && /^[a-zA-Z]+[0-9]{3}$/.test(vid)) vid = vid.replace(/([a-zA-Z]+)([0-9]{3})/, '$100$2');
if (vid) largeUrl = `https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/${vid}/${vid}pl.jpg`;
} else if (smallUrl.match(/jm\.jpg$/i)) {
largeUrl = smallUrl.replace(/jm\.jpg$/i, 'jp.jpg');
} else if (smallUrl.match(/ps\.jpg$/i)) {
largeUrl = smallUrl.replace(/ps\.jpg$/i, 'pl.jpg');
}
imageLink.src = largeUrl;
imageLink.classList.add('large-image');
imageLink.dataset.originalSrc = smallUrl;
const imgContainerDiv = imageLink.closest('.flex.items-center.justify-center.bg-base2, .flex.items-center.justify-center.bg-base-2');
if (imgContainerDiv) {
imgContainerDiv.innerHTML = '';
imgContainerDiv.appendChild(imageLink);
Object.assign(imgContainerDiv.style, { width: '100%', height: 'auto', display: 'block', padding: '0' });
}
imageLink.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
const allImages = Array.from(document.querySelectorAll('.large-image'));
let currentIndex = allImages.indexOf(imageLink);
if (currentIndex === -1) currentIndex = 0;
const mediaData = allImages.map(imgEl => ({
type: 'image',
thumbSrc: imgEl.src,
getLargeUrl: async () => {
let finalUrl = imgEl.src;
const sUrl = imgEl.dataset.originalSrc || imgEl.src;
const lUrl = getLargerImageUrl(sUrl);
if (lUrl && await checkImageExists(lUrl)) finalUrl = lUrl;
return finalUrl;
}
}));
buildUnifiedGalleryOverlay(mediaData, currentIndex);
});
const imageTitleContainer = document.createElement('div');
imageTitleContainer.className = 'image-title-container';
if (imgContainerDiv) imageTitleContainer.appendChild(imgContainerDiv);
titleLink.className = titleLink.className.replace(/text-md/g, 'title-below-image');
const titleContainer = titleLink.parentElement;
if (titleContainer) {
Object.assign(titleContainer.style, { display: 'block', margin: '0' });
imageTitleContainer.appendChild(titleContainer);
}
const flexContainer = container.querySelector('.flex.min-w-0.border-y.border-light');
if (flexContainer) {
flexContainer.innerHTML = '';
flexContainer.appendChild(imageTitleContainer);
flexContainer.style.display = 'block';
}
container.style.display = 'block';
}
});
applyPinterestLayout();
}
function initialize() {
if (!isDetailPage()) {
enhanceImages();
applyPinterestLayout();
} else {
enhanceDetails();
}
let lastUrl = location.href;
let debounceTimer;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
if (!isDetailPage()) {
enhanceImages();
applyPinterestLayout();
} else {
enhanceDetails();
}
}
}).observe(document, { subtree: true, childList: true });
new MutationObserver(() => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
if (!isDetailPage()) {
enhanceImages();
applyPinterestLayout();
} else {
enhanceDetails();
}
}, 150);
}).observe(document.body, { childList: true, subtree: true });
}
initialize();
})();