AvBase Ultimate Enhancer

Enhances avbase.net with video previews, zoom sliders, Sukebei RSS results in a styled table, larger images, titles below images, original size image popups, and adds Jable, Missav, and videoId copy buttons with 404 transparency at the correct position.

// ==UserScript==
// @name         AvBase Ultimate Enhancer
// @namespace    http://tampermonkey.net/
// @version      2.3.2
// @description  Enhances avbase.net with video previews, zoom sliders, Sukebei RSS results in a styled table, larger images, titles below images, original size image popups, and adds Jable, Missav, and videoId copy buttons with 404 transparency at the correct position.
// @match        https://www.avbase.net/*
// @grant        GM_addStyle
// @grant        GM_xmlhttpRequest
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 合併後的樣式
    GM_addStyle(`
        .adaptive-container {
            height: auto !important;
            min-height: 100%;
            width: 100%;
            display: flex;
            flex-direction: column;
            grid-column: 1 / -1;
            align-items: center;
            overflow: visible;
        }
        .vertical-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 100%;
            overflow-x: auto;
            overflow-y: visible;
            height: auto;
        }
        .image-container {
            display: flex;
            flex-direction: column;
            align-items: center;
            width: 100%;
            gap: 1px;
            height: auto;
        }
        .scene-container {
            width: 100%;
            max-width: 800px;
            height: auto;
            min-height: 450px;
        }
        .table-wrapper {
            width: 100%;
            padding: 10px;
            height: auto;
        }
        .toggle-button {
            padding: 5px 295px;
            background-color: #1e293b;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            margin: 5px 0;
        }
        .toggle-button img {
            width: 20px;
            height: 20px;
            filter: brightness(100%) invert(1);
            transition: transform 0.2s ease;
        }
        .toggle-button:active img {
            transform: scale(1.2);
        }
        .controls-bar {
            width: 100%;
            max-width: 800px;
            background-color: #000000;
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 5px 10px;
            margin: 0;
            box-sizing: border-box;
        }
        .controls-bar button {
            background-color: #000000;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            padding: 5px;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: transform 0.2s ease;
            margin-right: 8px;
        }
        .controls-bar button:last-child {
            margin-right: 0;
        }
        .controls-bar button img {
            width: 20px;
            height: 20px;
            filter: brightness(100%) invert(1);
        }
        .controls-bar button:active {
            transform: scale(1.2);
        }
        .zoom-slider-container {
            display: flex;
            align-items: center;
            position: relative;
            margin: 0;
        }
        .zoom-button {
            background-color: #000000;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            padding: 5px;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 8px;
        }
        .zoom-button img {
            width: 20px;
            height: 20px;
            filter: brightness(100%) invert(1);
        }
        .zoom-slider {
            -webkit-appearance: none;
            appearance: none;
            width: 0;
            height: 6px;
            background: #1e293b;
            outline: none;
            opacity: 0;
            transition: opacity 0.3s ease, width 0.3s ease;
            border-radius: 4px;
            margin: 0 5px;
            vertical-align: middle;
        }
        .zoom-slider.show {
            opacity: 1;
            width: 70px;
        }
        .zoom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 12px;
            height: 12px;
            background: #fff;
            cursor: pointer;
            border-radius: 50%;
        }
        .zoom-slider::-moz-range-thumb {
            width: 12px;
            height: 12px;
            background: #fff;
            cursor: pointer;
            border-radius: 50%;
        }
        .zoom-value {
            color: white;
            font-size: 12px;
            margin-right: 5px;
            width: 40px;
            text-align: right;
            opacity: 0;
            transition: opacity 0.3s ease;
            vertical-align: middle;
        }
        .zoom-value.show {
            opacity: 1;
        }
        .volume-slider-container {
            display: flex;
            align-items: center;
            position: relative;
        }
        .volume-slider {
            -webkit-appearance: none;
            appearance: none;
            width: 0;
            height: 6px;
            background: #1e293b;
            outline: none;
            opacity: 0;
            transition: opacity 0.3s ease, width 0.3s ease;
            border-radius: 4px;
            margin: 0 5px;
        }
        .volume-slider.show {
            opacity: 1;
            width: 70px;
        }
        .volume-slider:hover {
            opacity: 1;
        }
        .volume-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 12px;
            height: 12px;
            background: #fff;
            cursor: pointer;
            border-radius: 50%;
        }
        .volume-slider::-moz-range-thumb {
            width: 16px;
            height: 16px;
            background: #fff;
            cursor: pointer;
            border-radius: 50%;
        }
        .fullscreen-button {
            background-color: #000000;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            padding: 5px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .fullscreen-button img {
            width: 20px;
            height: 20px;
            filter: brightness(100%) invert(1);
        }
        .table-wrapper svg {
            fill: white;
            width: 20px;
            height: 20px;
        }
        @media (min-width: 1024px) {
            .cl-container {
                width: 100%;
            }
        }
        .sm\\:grid-cols-2 {
            grid-template-columns: repeat(6, minmax(0, 1fr));
        }
        .sm\\:grid-cols-3 {
            grid-template-columns: repeat(6, minmax(0, 1fr));
        }
        .large-image {
            width: 100%;
            max-width: 100%;
            height: auto;
            object-fit: contain;
            display: block;
            border-radius: 4px;
            cursor: zoom-in;
        }
        .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: center;
            color: #333;
            text-decoration: none;
            font-weight: bold;
            width: 100%;
            box-sizing: border-box;
        }
        .image-title-container {
            display: block;
            width: 100%;
            text-align: center;
        }
        .overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
            opacity: 0;
            transition: opacity 0.3s ease;
        }
        .overlay.show {
            opacity: 1;
        }
        .overlay-image {
            max-width: 90%;
            max-height: 90%;
            width: auto;
            height: auto;
            object-fit: contain;
            cursor: zoom-out;
            border-radius: 4px;
        }
        a-scene .a-canvas + div {
            display: none !important;
        }
        .jable-btn, .missav-btn, .copy-btn {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 2rem;
            line-height: 2rem;
            text-align: center;
            background-color: #4b5563;
            color: white;
            border-radius: 0.25rem;
            font-size: 0.75rem;
            font-weight: normal;
            padding: 0 0.5rem;
            cursor: pointer;
        }
        .jable-btn, .missav-btn {
            width: 2rem;
        }
        .copy-btn {
            max-width: 10rem;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
        }
        .transparent {
            background-color: transparent !important;
        }
        .jable-btn a, .missav-btn a {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 100%;
            height: 100%;
            color: inherit;
            text-decoration: none;
        }
    `);

    // 動態載入 A-Frame 庫
    function loadAFrame() {
        const aframeScript = document.createElement('script');
        aframeScript.src = 'https://aframe.io/releases/1.7.0/aframe.min.js';
        document.head.appendChild(aframeScript);
    }

    // 處理 DMM 鏈接
    function processDmmLink(link) {
        try {
            if (link.includes('al.dmm.co.jp')) {
                const urlParams = new URLSearchParams(new URL(link).search);
                const acred = urlParams.get('acred');
                if (acred) return decodeURIComponent(acred);
                const lurl = urlParams.get('lurl');
                if (lurl) return decodeURIComponent(lurl);
            }
            return link;
        } catch (error) {
            console.error('處理 DMM 鏈接時出錯:', error);
            return link;
        }
    }

    // 檢查圖片是否存在
    function checkImageExists(url) {
        return new Promise((resolve) => {
            const img = new Image();
            img.onload = () => resolve(true);
            img.onerror = () => resolve(false);
            img.src = url;
            setTimeout(() => resolve(false), 5000);
        });
    }

    // 檢查視頻可用性
    async function checkVideoAvailability(url) {
        try {
            const response = await fetch(url, { method: 'HEAD' });
            return response.ok;
        } catch (error) {
            console.error(`檢查 ${url} 失敗:`, error);
            return false;
        }
    }

    // 從圖片 URL 提取影片 ID 並構造更大圖片 URL
    function getLargerImageUrl(smallImageUrl) {
        let match = smallImageUrl.match(/\/([^\/]+)(ps|jm)\.jpg$/i);
        if (!match) return null;
        let videoId = match[1];
        const suffix = match[2].toLowerCase();

        if (suffix === 'ps' && smallImageUrl.includes('awsimgsrc.dmm.co.jp')) {
            if (/^[a-zA-Z]+[0-9]{3}$/.test(videoId)) {
                videoId = videoId.replace(/([a-zA-Z]+)([0-9]{3})/, '$100$2');
            }
            return `https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/${videoId}/${videoId}pl.jpg`;
        }
        if (suffix === 'jm' && smallImageUrl.includes('pics.dmm.co.jp')) {
            return smallImageUrl.replace(/jm\.jpg$/i, 'jp.jpg');
        }
        return smallImageUrl.replace(/ps\.jpg$/i, 'pl.jpg');
    }

    // 獲取 Sukebei RSS 結果
    function fetchSukebeiResults(videoId) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://sukebei.nyaa.si/?page=rss&q=${encodeURIComponent(videoId)}`,
                headers: {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
                    "Referer": "https://sukebei.nyaa.si/"
                },
                onload: function(response) {
                    if (response.status === 200) {
                        let parser = new DOMParser();
                        let xmlDoc = parser.parseFromString(response.responseText, "text/xml");
                        let items = xmlDoc.getElementsByTagName("item");
                        let results = [];
                        for (let i = 0; i < items.length; i++) {
                            let item = items[i];
                            let title = item.getElementsByTagName("title")[0]?.textContent.trim() || "";
                            let link = item.getElementsByTagName("link")[0]?.textContent.trim() || "";
                            let size = item.getElementsByTagName("nyaa:size")[0]?.textContent.trim() || "";
                            let pubDate = item.getElementsByTagName("pubDate")[0]?.textContent.trim() || "";
                            let infoHash = item.getElementsByTagName("nyaa:infoHash")[0]?.textContent.trim() || "";
                            let magnet = infoHash ? "magnet:?xt=urn:btih:" + infoHash : "";
                            results.push({ name: title, link: link, size: size, date: pubDate, magnet: magnet });
                        }
                        resolve(results);
                    } else {
                        reject(`HTTP error: ${response.status}`);
                    }
                },
                onerror: function(err) {
                    reject(err);
                }
            });
        });
    }

    // 檢查 Jable 是否返回 404
    function checkJableAvailability(videoId) {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://jable.tv/videos/${encodeURIComponent(videoId)}/`,
                onload: response => {
                    const is404 = response.status === 404 ||
                                  response.responseText.includes('<title>404') ||
                                  response.responseText.includes('<h1>Not Found</h1>');
                    resolve(!is404);
                },
                onerror: () => resolve(false)
            });
        });
    }

    // 檢查 Missav 是否返回 404
    function checkMissavAvailability(videoId) {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: `https://missav.ai/${encodeURIComponent(videoId)}`,
                onload: response => {
                    const is404 = response.responseText.includes('404') ||
                                  response.responseText.includes('找不到頁面');
                    resolve(!is404);
                },
                onerror: () => resolve(false)
            });
        });
    }

    // 圖片增強功能
    function enhanceImages() {
        const resultContainers = document.querySelectorAll('.bg-base.border.border-light.rounded-lg.overflow-hidden');
        if (!resultContainers.length) return;

        resultContainers.forEach((container) => {
            const imageLink = container.querySelector('div.flex.items-center.justify-center.bg-base2 a[rel="noopener noreferrer sponsored"] img');
            const titleLink = container.querySelector('a.text-md.font-bold.btn-ghost.rounded-lg.m-1[class*="line-clamp"]');

            if (imageLink && titleLink) {
                const smallImageUrl = imageLink.src;
                let largeImageUrl = smallImageUrl;

                if (smallImageUrl.match(/ps\.jpg$/i) && smallImageUrl.includes('awsimgsrc.dmm.co.jp')) {
                    let videoId = smallImageUrl.match(/\/([^\/]+)ps\.jpg$/i)[1];
                    if (/^[a-zA-Z]+[0-9]{3}$/.test(videoId)) {
                        videoId = videoId.replace(/([a-zA-Z]+)([0-9]{3})/, '$100$2');
                    }
                    largeImageUrl = `https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/${videoId}/${videoId}pl.jpg`;
                } else if (smallImageUrl.match(/jm\.jpg$/i) && smallImageUrl.includes('pics.dmm.co.jp')) {
                    largeImageUrl = smallImageUrl.replace(/jm\.jpg$/i, 'jp.jpg');
                } else if (smallImageUrl.match(/ps\.jpg$/i)) {
                    largeImageUrl = smallImageUrl.replace(/ps\.jpg$/i, 'pl.jpg');
                }

                imageLink.src = largeImageUrl;
                imageLink.classList.add('large-image');

                const imageAnchor = imageLink.parentElement;
                const imageContainerDiv = imageAnchor.closest('.flex.items-center.justify-center.bg-base2.grow-0.shrink-0.w-28.h-40.basis-28');
                if (imageContainerDiv) {
                    imageContainerDiv.innerHTML = '';
                    imageContainerDiv.appendChild(imageLink);
                    imageContainerDiv.style.width = '100%';
                    imageContainerDiv.style.height = 'auto';
                    imageContainerDiv.style.display = 'block';
                    imageContainerDiv.style.padding = '0';
                }

                imageLink.addEventListener('click', async () => {
                    const overlay = document.createElement('div');
                    overlay.className = 'overlay';
                    const overlayImage = document.createElement('img');
                    overlayImage.className = 'overlay-image';

                    let finalImageUrl = largeImageUrl;
                    const largerImageUrl = getLargerImageUrl(smallImageUrl);
                    if (largerImageUrl) {
                        const largerImageExists = await checkImageExists(largerImageUrl);
                        if (largerImageExists) finalImageUrl = largerImageUrl;
                    }

                    if (smallImageUrl.match(/ps\.jpg$/i)) {
                        let videoId = smallImageUrl.match(/\/([^\/]+)ps\.jpg$/i)[1];
                        if (/^[a-zA-Z]+[0-9]{3}$/.test(videoId)) {
                            videoId = videoId.replace(/([a-zA-Z]+)([0-9]{3})/, '$100$2');
                        }
                        const awsPlImageUrl = `https://awsimgsrc.dmm.co.jp/pics_dig/digital/video/${videoId}/${videoId}pl.jpg`;
                        const awsImageExists = await checkImageExists(awsPlImageUrl);
                        if (awsImageExists) finalImageUrl = awsPlImageUrl;
                    }

                    overlayImage.src = finalImageUrl;
                    overlay.appendChild(overlayImage);
                    document.body.appendChild(overlay);
                    setTimeout(() => overlay.classList.add('show'), 10);

                    overlay.addEventListener('click', () => {
                        overlay.classList.remove('show');
                        setTimeout(() => overlay.remove(), 300);
                    });
                });

                const imageTitleContainer = document.createElement('div');
                imageTitleContainer.className = 'image-title-container';
                imageTitleContainer.appendChild(imageContainerDiv);

                titleLink.classList.remove('text-md');
                titleLink.classList.add('title-below-image');
                const titleContainer = titleLink.parentElement;
                titleContainer.style.display = 'block';
                titleContainer.style.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';
            }
        });
    }

    // 視頻和詳情頁增強功能
    async function enhanceDetails() {
        if (!window.location.href.startsWith('https://www.avbase.net/works/')) return;

        loadAFrame();

        const container = document.querySelector('.flex.overflow-x-auto.overflow-y-hidden');
        if (!container) return;

        const titleLink = document.querySelector('h1.text-lg')?.parentElement;
        if (titleLink && titleLink.href && titleLink.href.includes('al.dmm.co.jp')) {
            titleLink.href = processDmmLink(titleLink.href);
        }

        const verticalContainer = document.createElement('div');
        verticalContainer.className = 'vertical-container';
        container.parentNode.replaceChild(verticalContainer, container);

        const parentContainer = verticalContainer.closest('.h-44.w-full.flex');
        if (parentContainer) {
            parentContainer.className = 'adaptive-container bg-base-300';
        }

        const codeElement = document.querySelector('span[dir="rtl"].pl-1.whitespace-nowrap.overflow-hidden.text-ellipsis');
        const code = codeElement ? codeElement.textContent.trim() : null;
        if (!code) return;

        // 添加 Jable、Missav 和複製按鈕
        const buttonContainer = document.querySelector('div.flex.gap-2.items-center');
        if (buttonContainer) {
            const targetSpan = buttonContainer.querySelector('span.text-xs.flex');
            if (targetSpan) {
                const videoIdElement = document.querySelector('div.flex.gap-2.items-center span.text-xs div span:not(.text-gray-400)') ||
                                      document.querySelector('span[dir="rtl"].pl-1.whitespace-nowrap.overflow-hidden.text-ellipsis');
                const videoId = videoIdElement ? videoIdElement.textContent.trim() : null;

                if (videoId && !buttonContainer.querySelector('.jable-btn') && !buttonContainer.querySelector('.missav-btn') && !buttonContainer.querySelector('.copy-btn')) {
                    const encodedVideoId = encodeURIComponent(videoId);

                    // Jable 按鈕
                    const jableButton = document.createElement('div');
                    jableButton.className = 'jable-btn';
                    const jableLink = document.createElement('a');
                    jableLink.href = `https://jable.tv/videos/${encodedVideoId}/`;
                    jableLink.target = '_blank';
                    jableLink.rel = 'noopener noreferrer';
                    jableLink.textContent = 'J';
                    jableButton.appendChild(jableLink);

                    // Missav 按鈕
                    const missavButton = document.createElement('div');
                    missavButton.className = 'missav-btn';
                    const missavLink = document.createElement('a');
                    missavLink.href = `https://missav.ai/${encodedVideoId}`;
                    missavLink.target = '_blank';
                    missavLink.rel = 'noopener noreferrer';
                    missavLink.textContent = 'M';
                    missavButton.appendChild(missavLink);

                    // 複製按鈕
                    const copyButton = document.createElement('div');
                    copyButton.className = 'copy-btn';
                    copyButton.textContent = encodedVideoId;
                    copyButton.title = '點擊複製 videoId';

                    // 動態設置按鈕寬度
                    const charWidth = 0.5;
                    const padding = 1;
                    const textWidth = encodedVideoId.length * charWidth;
                    const copiedWidth = 3 * charWidth;
                    const buttonWidth = Math.max(textWidth, copiedWidth) + padding;
                    copyButton.style.width = `${buttonWidth}rem`;

                    copyButton.addEventListener('click', () => {
                        navigator.clipboard.writeText(encodedVideoId).then(() => {
                            copyButton.textContent = '已復製';
                            setTimeout(() => copyButton.textContent = encodedVideoId, 1500);
                        }).catch(err => console.error('複製失敗:', err));
                    });

                    // 插入按鈕
                    buttonContainer.insertBefore(copyButton, targetSpan);
                    buttonContainer.insertBefore(missavButton, copyButton);
                    buttonContainer.insertBefore(jableButton, missavButton);

                    // 檢查 404 並設置背景
                    Promise.all([checkJableAvailability(videoId), checkMissavAvailability(videoId)])
                        .then(([jableAvailable, missavAvailable]) => {
                            if (!jableAvailable) jableButton.classList.add('transparent');
                            if (!missavAvailable) missavButton.classList.add('transparent');
                        });
                }
            }
        }

        let videoUrls = [];
        const isVR = code.toLowerCase().includes('vr') || code.toLowerCase().includes('aqu') || code.toLowerCase().includes('exmo') || 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`);
        }
        if (isVR) {
            videoUrls.push(`https://cc3001.dmm.com/vrsample/${code.charAt(0)}/${prefix}/${code}/${code}vrlite.mp4`);
        } else {
            videoUrls.push(`https://cc3001.dmm.com/litevideo/freepv/${code.charAt(0)}/${prefix}/${code}/${code}hhb.mp4`);
        }
        videoUrls.push(`https://cc3001.dmm.com/litevideo/freepv/${code.charAt(0)}/${prefix}/${code}/${code}_dmb_w.mp4`);
        if (code.includes('_')) {
            videoUrls.push(`https://cc3001.dmm.com/vrsample/${code.charAt(0)}/${prefix}/${code}/${code}vrlite.mp4`);
        }

        const availabilityChecks = videoUrls.map(url => checkVideoAvailability(url));
        const results = await Promise.all(availabilityChecks);
        const availableVideoUrl = videoUrls[results.findIndex(result => result)];

        if (availableVideoUrl) {
            const sceneContainer = document.createElement('div');
            sceneContainer.className = 'scene-container';
            verticalContainer.appendChild(sceneContainer);

            let isVRMode = isVR;
            let cameraZoom = 160;
            let lastVolume = 1;

            function initializePlayer() {
                sceneContainer.innerHTML = '';
                if (isVRMode) {
                    const scene = document.createElement('a-scene');
                    scene.setAttribute('embedded', '');
                    scene.setAttribute('vr-mode-ui', 'enabled: false');
                    sceneContainer.appendChild(scene);

                    const videoSphere = document.createElement('a-videosphere');
                    videoSphere.setAttribute('src', availableVideoUrl);
                    videoSphere.setAttribute('rotation', '0 -180 0');
                    videoSphere.setAttribute('phi-start', '0');
                    videoSphere.setAttribute('phi-length', '180');
                    videoSphere.setAttribute('autoplay', '');
                    videoSphere.setAttribute('muted', '');
                    scene.appendChild(videoSphere);

                    const camera = document.createElement('a-camera');
                    camera.setAttribute('position', `0 0 ${cameraZoom}`);
                    camera.setAttribute('rotation', '0 90 0');
                    scene.appendChild(camera);
                } else {
                    const videoElement = document.createElement('video');
                    videoElement.src = availableVideoUrl;
                    videoElement.controls = true;
                    videoElement.style.width = '100%';
                    videoElement.style.maxWidth = '800px';
                    videoElement.style.height = 'auto';
                    videoElement.style.aspectRatio = '16/9';
                    sceneContainer.appendChild(videoElement);
                    videoElement.addEventListener('loadeddata', () => videoElement.play());
                }
            }
            initializePlayer();

            const controlsBar = document.createElement('div');
            controlsBar.className = 'controls-bar';
            verticalContainer.appendChild(controlsBar);

            const leftControls = document.createElement('div');
            leftControls.style.display = 'flex';
            leftControls.style.alignItems = 'center';
            controlsBar.appendChild(leftControls);

            const rightControls = document.createElement('div');
            rightControls.style.display = 'flex';
            rightControls.style.alignItems = 'center';
            controlsBar.appendChild(rightControls);

            function createButton(svgUrl, onClick, comment) {
                const button = document.createElement('button');
                const img = document.createElement('img');
                img.src = svgUrl;
                img.style.width = '20px';
                img.style.height = '20px';
                button.appendChild(img);
                button.addEventListener('click', onClick);
                return button;
            }

            const playPauseButton = createButton(
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/play.svg',
                () => {
                    const video = isVRMode ?
                        sceneContainer.querySelector('a-videosphere').components.material.material.map.image :
                        sceneContainer.querySelector('video');
                    if (video.paused) {
                        video.play();
                        playPauseButton.firstChild.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/pause.svg';
                    } else {
                        video.pause();
                        playPauseButton.firstChild.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/play.svg';
                    }
                },
                '播放/暫停按鈕'
            );
            leftControls.appendChild(playPauseButton);

            const rewindButton = createButton(
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/seek-backward-10.svg',
                () => {
                    const video = isVRMode ?
                        sceneContainer.querySelector('a-videosphere').components.material.material.map.image :
                        sceneContainer.querySelector('video');
                    video.currentTime = Math.max(0, video.currentTime - 5);
                },
                '倒退5秒按鈕'
            );
            leftControls.appendChild(rewindButton);

            const forwardButton = createButton(
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/seek-forward-10.svg',
                () => {
                    const video = isVRMode ?
                        sceneContainer.querySelector('a-videosphere').components.material.material.map.image :
                        sceneContainer.querySelector('video');
                    video.currentTime = Math.min(video.duration, video.currentTime + 5);
                },
                '快進5秒按鈕'
            );
            leftControls.appendChild(forwardButton);

            const volumeContainer = document.createElement('div');
            volumeContainer.className = 'volume-slider-container';
            const volumeButton = createButton(
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg',
                () => {
                    const video = isVRMode ?
                        sceneContainer.querySelector('a-videosphere').components.material.material.map.image :
                        sceneContainer.querySelector('video');
                    if (video.muted) {
                        video.muted = false;
                        video.volume = lastVolume;
                        volumeButton.firstChild.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg';
                    } else {
                        lastVolume = video.volume;
                        video.muted = true;
                        volumeButton.firstChild.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/mute.svg';
                    }
                },
                '音量開關按鈕'
            );
            const volumeSlider = document.createElement('input');
            volumeSlider.type = 'range';
            volumeSlider.className = 'volume-slider';
            volumeSlider.min = '0';
            volumeSlider.max = '1';
            volumeSlider.step = '0.01';
            volumeSlider.value = '1';
            volumeSlider.addEventListener('input', () => {
                const video = isVRMode ?
                    sceneContainer.querySelector('a-videosphere').components.material.material.map.image :
                    sceneContainer.querySelector('video');
                video.muted = false;
                video.volume = parseFloat(volumeSlider.value);
                lastVolume = video.volume;
                volumeButton.firstChild.src = video.volume === 0 ?
                    'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/mute.svg' :
                    'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/volume-high.svg';
            });
            volumeContainer.appendChild(volumeButton);
            volumeContainer.appendChild(volumeSlider);
            volumeContainer.addEventListener('mouseenter', () => {
                volumeSlider.classList.add('show');
            });
            volumeContainer.addEventListener('mouseleave', () => {
                volumeSlider.classList.remove('show');
            });
            leftControls.appendChild(volumeContainer);

            const zoomContainer = document.createElement('div');
            zoomContainer.className = 'zoom-slider-container';
            const zoomButton = document.createElement('button');
            zoomButton.className = 'zoom-button';
            const zoomImg = document.createElement('img');
            zoomImg.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/search.svg';
            zoomImg.style.width = '20px';
            zoomImg.style.height = '20px';
            zoomButton.appendChild(zoomImg);
            const zoomSlider = document.createElement('input');
            zoomSlider.type = 'range';
            zoomSlider.className = 'zoom-slider';
            zoomSlider.min = '-480';
            zoomSlider.max = '480';
            zoomSlider.step = '10';
            zoomSlider.value = '160';
            const zoomValue = document.createElement('span');
            zoomValue.className = 'zoom-value';
            zoomValue.textContent = '160';
            zoomButton.addEventListener('click', () => {
                zoomSlider.classList.toggle('show');
                zoomValue.classList.toggle('show');
            });
            zoomSlider.addEventListener('input', () => {
                if (!isVRMode) return;
                const camera = sceneContainer.querySelector('a-camera');
                if (!camera) return;
                cameraZoom = parseFloat(zoomSlider.value);
                camera.setAttribute('position', `0 0 ${cameraZoom}`);
                zoomValue.textContent = cameraZoom;
            });
            zoomContainer.appendChild(zoomValue);
            zoomContainer.appendChild(zoomSlider);
            zoomContainer.appendChild(zoomButton);
            rightControls.appendChild(zoomContainer);

            const togglePlayerButton = createButton(
                isVRMode ? 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/2d-label-icon.svg' : 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/vr-label-icon.svg',
                () => {
                    const video = isVRMode ?
                        sceneContainer.querySelector('a-videosphere')?.components.material.material.map.image :
                        sceneContainer.querySelector('video');
                    if (video) {
                        video.pause();
                        video.currentTime = 0;
                    }
                    isVRMode = !isVRMode;
                    initializePlayer();
                    togglePlayerButton.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';
                },
                '2D/VR切換按鈕'
            );
            rightControls.appendChild(togglePlayerButton);

            const fullscreenButton = createButton(
                'https://raw.githubusercontent.com/leogfa/svg/65fdb85e8047e4b5b6e221e5516962c534d8efb6/fullscreen.svg',
                () => {
                    if (!document.fullscreenElement) {
                        sceneContainer.requestFullscreen().catch(err => {
                            console.error('無法進入全螢幕:', err);
                        });
                    } else {
                        document.exitFullscreen();
                    }
                },
                '全螢幕切換按鈕'
            );
            fullscreenButton.className = 'fullscreen-button';
            rightControls.appendChild(fullscreenButton);
        }

        const imageContainer = document.createElement('div');
        imageContainer.className = 'image-container';
        imageContainer.style.display = 'none';
        verticalContainer.appendChild(imageContainer);

        container.querySelectorAll('a').forEach(link => {
            let newSrc = link.href;
            if (newSrc.includes('al.dmm.co.jp')) {
                newSrc = processDmmLink(newSrc);
            }
            const img = link.querySelector('img');
            if (img) {
                img.src = newSrc;
                img.style.width = 'auto';
                img.style.height = 'auto';
                img.style.maxWidth = '100%';
                img.style.objectFit = 'contain';
            }
            link.href = newSrc;
            link.style.display = 'block';
            link.style.width = 'auto';
            link.style.height = 'auto';
            imageContainer.appendChild(link);
        });

        const toggleButton = document.createElement('button');
        const toggleImg = document.createElement('img');
        toggleImg.src = 'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/eye.svg';
        toggleImg.style.width = '20px';
        toggleImg.style.height = '20px';
        toggleButton.appendChild(toggleImg);
        toggleButton.className = 'toggle-button';
        toggleButton.addEventListener('click', () => {
            imageContainer.style.display = imageContainer.style.display === 'none' ? 'flex' : 'none';
            toggleImg.src = imageContainer.style.display === 'none' ?
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/eye.svg' :
                'https://raw.githubusercontent.com/leogfa/svg/b99823546f418d86bc5c3ecdd53b7c02e38cad9c/no-eye.svg';
            verticalContainer.style.height = 'auto';
            parentContainer.style.height = 'auto';
        });
        verticalContainer.insertBefore(toggleButton, imageContainer);

        const videoIdElement = document.querySelector('div.flex.gap-2.items-center span.text-xs div span:not(.text-gray-400)');
        const videoId = videoIdElement ? videoIdElement.textContent.trim() : null;
        if (videoId) {
            fetchSukebeiResults(videoId).then(results => {
                let tableWrapper = document.createElement('div');
                tableWrapper.className = 'table-wrapper';

                let table = document.createElement('table');
                table.style.width = '100%';
                table.style.borderCollapse = 'collapse';
                table.style.fontSize = '12px';
                table.style.margin = '0';

                if (results.length) {
                    results.forEach(result => {
                        let tr = document.createElement('tr');

                        let tdName = document.createElement('td');
                        tdName.style.padding = '8px';
                        tdName.style.border = '1px solid #ddd';
                        tdName.style.width = '60%';
                        tdName.textContent = result.name;
                        tr.appendChild(tdName);

                        let tdLink = document.createElement('td');
                        tdLink.style.padding = '8px';
                        tdLink.style.border = '1px solid #ddd';
                        tdLink.style.textAlign = 'center';
                        tdLink.style.width = '15%';
                        let iconWrapper = document.createElement('div');
                        iconWrapper.style.display = 'inline-flex';
                        iconWrapper.style.gap = '8px';
                        iconWrapper.style.alignItems = 'center';
                        let aTorrent = document.createElement('a');
                        aTorrent.href = result.link;
                        aTorrent.target = '_blank';
                        aTorrent.innerHTML = `<img src="https://raw.githubusercontent.com/leogfa/svg/fde17c8702542028c186c6fb170a8bc26a6c1be5/download.svg" style="width: 24px; height: 24px; filter: brightness(100%) invert(1);">`;
                        iconWrapper.appendChild(aTorrent);
                        let btnMagnet = document.createElement('button');
                        btnMagnet.style.background = 'none';
                        btnMagnet.style.border = 'none';
                        btnMagnet.style.cursor = 'pointer';
                        btnMagnet.innerHTML = `<img src="https://raw.githubusercontent.com/leogfa/svg/3edada4e4c0a5c1a83938a427459a488d22ec6a4/magnet.svg" style="width: 24px; height: 24px; filter: brightness(100%) invert(1);">`;
                        btnMagnet.addEventListener('click', () => {
                            if (navigator.clipboard) {
                                navigator.clipboard.writeText(result.magnet).then(() => {
                                    btnMagnet.textContent = '已複製';
                                    setTimeout(() => {
                                        btnMagnet.innerHTML = `<img src="https://raw.githubusercontent.com/leogfa/svg/3edada4e4c0a5c1a83938a427459a488d22ec6a4/magnet.svg" style="width: 24px; height: 24px; filter: brightness(100%) invert(1);">`;
                                    }, 1500);
                                });
                            }
                        });
                        iconWrapper.appendChild(btnMagnet);
                        tdLink.appendChild(iconWrapper);
                        tr.appendChild(tdLink);

                        let tdSize = document.createElement('td');
                        tdSize.style.padding = '8px';
                        tdSize.style.border = '1px solid #ddd';
                        tdSize.style.textAlign = 'center';
                        tdSize.style.width = '10%';
                        tdSize.textContent = result.size;
                        tr.appendChild(tdSize);

                        let tdDate = document.createElement('td');
                        tdDate.style.padding = '8px';
                        tdDate.style.border = '1px solid #ddd';
                        tdDate.style.textAlign = 'center';
                        tdDate.style.width = '15%';
                        let d = new Date(result.date);
                        function pad(n) { return n < 10 ? "0" + n : n; }
                        let formattedDate = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`;
                        tdDate.textContent = formattedDate;
                        tr.appendChild(tdDate);

                        table.appendChild(tr);
                    });
                } else {
                    let noResultRow = document.createElement('tr');
                    let noResultTd = document.createElement('td');
                    noResultTd.colSpan = "4";
                    noResultTd.style.padding = '8px';
                    noResultTd.style.textAlign = 'center';
                    noResultTd.textContent = '找不到Seed';
                    noResultRow.appendChild(noResultTd);
                    table.appendChild(noResultRow);
                }
                tableWrapper.appendChild(table);
                imageContainer.insertAdjacentElement('afterend', tableWrapper);
            }).catch(err => {
                console.error('Sukebei RSS 搜尋失敗: ', err);
            });
        }
    }

    // 主初始化函數
    function initialize() {
        if (!window.location.href.startsWith('https://www.avbase.net/works/')) {
            enhanceImages();
        }
        enhanceDetails();

        let lastUrl = location.href;
        const urlObserver = new MutationObserver(() => {
            const url = location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                enhanceImages();
                enhanceDetails();
            }
        });
        urlObserver.observe(document, { subtree: true, childList: true });

        const contentObserver = new MutationObserver(() => {
            if (!window.location.href.startsWith('https://www.avbase.net/works/')) {
                enhanceImages();
            } else {
                enhanceDetails();
            }
        });
        contentObserver.observe(document.body, { childList: true, subtree: true });
    }

    initialize();
})();