AvBase Ultimate Enhancer

Combines BetterAvBase and AvBase Image Enhancer: Enhances avbase.net with video previews, zoom sliders, Sukebei RSS results in a styled table, larger images, titles below images, and original size image popups.

Ekde 2025/05/28. Vidu La ĝisdata versio.

// ==UserScript==
// @name         AvBase Ultimate Enhancer
// @namespace    http://tampermonkey.net/
// @version      2.2.4
// @description  Combines BetterAvBase and AvBase Image Enhancer: Enhances avbase.net with video previews, zoom sliders, Sukebei RSS results in a styled table, larger images, titles below images, and original size image popups.
// @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));
        }
        .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;
        }
    `);

    // 動態載入 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);
                }
            });
        });
    }

    // 圖片增強功能
    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;

        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);

            // 2D/VR 切換按鈕
            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 = `<svg width="24" height="24" viewBox="0 0 24 24" fill="white" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M3 14.25C3.41421 14.25 3.75 14.5858 3.75 15C3.75 16.4354 3.75159 17.4365 3.85315 18.1919C3.9518 18.9257 4.13225 19.3142 4.40901 19.591C4.68577 19.8678 5.07435 20.0482 5.80812 20.1469C6.56347 20.2484 7.56459 20.25 9 20.25H15C16.4354 20.25 17.4365 20.2484 18.1919 20.1469C18.9257 20.0482 19.3142 19.8678 19.591 19.591C19.8678 19.3142 20.0482 18.9257 20.1469 18.1919C20.2484 17.4365 20.25 16.4354 20.25 15C20.25 14.5858 20.5858 14.25 21 14.25C21.4142 14.25 21.75 14.5858 21.75 15V15.0549C21.75 16.4225 21.75 17.5248 21.6335 18.3918C21.5125 19.2919 21.2536 20.0497 20.6517 20.6516C20.0497 21.2536 19.2919 21.5125 18.3918 21.6335C17.5248 21.75 16.4225 21.75 15.0549 21.75H8.94513C7.57754 21.75 6.47522 21.75 5.60825 21.6335C4.70814 21.5125 3.95027 21.2536 3.34835 20.6517C2.74643 20.0497 2.48754 19.2919 2.36652 18.3918C2.24996 17.5248 2.24998 16.4225 2.25 15.0549C2.25 15.0366 2.25 15.0183 2.25 15C2.25 14.5858 2.58579 14.25 3 14.25Z"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 16.75C12.2106 16.75 12.4114 16.6615 12.5535 16.5061L16.5535 12.1311C16.833 11.8254 16.8118 11.351 16.5061 11.0715C16.2004 10.792 15.726 10.8132 15.4465 11.1189L12.75 14.0682V3C12.75 2.58579 12.4142 2.25 12 2.25C11.5858 2.25 11.25 2.58579 11.25 3V14.0682L8.55353 11.1189C8.27403 10.8132 7.79963 10.792 7.49393 11.0715C7.18823 11.351 7.16698 11.8254 7.44648 12.1311L11.4465 16.5061C11.5886 16.6615 11.7894 16.75 12 16.75Z"/>
</svg>`;
                        iconWrapper.appendChild(aTorrent);
                        let btnMagnet = document.createElement('button');
                        btnMagnet.style.background = 'none';
                        btnMagnet.style.border = 'none';
                        btnMagnet.style.cursor = 'pointer';
                        btnMagnet.innerHTML = `<svg width="24" height="24" viewBox="0 0 32 32" fill="white" xmlns="http://www.w3.org/2000/svg">
<path d="M30 1.25h-8c-0.414 0-0.75 0.336-0.75 0.75v0 14c0.003 0.067 0.005 0.145 0.005 0.224 0 2.779-2.253 5.031-5.031 5.031-0.079 0-0.157-0.002-0.235-0.005l0.011 0c-0.067 0.003-0.145 0.005-0.223 0.005-2.779 0-5.032-2.253-5.032-5.032 0-0.079 0.002-0.157 0.005-0.234l-0 0.011v-14c-0-0.414-0.336-0.75-0.75-0.75h-8c-0.414 0-0.75 0.336-0.75 0.75v0 14c-0.009 0.187-0.014 0.407-0.014 0.628 0 7.807 6.329 14.136 14.136 14.136 0.221 0 0.44-0.005 0.659-0.015l-0.031 0.001c0.187 0.009 0.407 0.014 0.627 0.014 7.808 0 14.137-6.329 14.137-14.137 0-0.221-0.005-0.44-0.015-0.658l0.001 0.031z"/>
</svg>`;
                        btnMagnet.addEventListener('click', () => {
                            if (navigator.clipboard) {
                                navigator.clipboard.writeText(result.magnet).then(() => {
                                    btnMagnet.textContent = '已複製';
                                    setTimeout(() => {
                                        btnMagnet.innerHTML = `<svg width="24" height="24" viewBox="0 0 32 32" fill="white" xmlns="http://www.w3.org/2000/svg">
<path d="M30 1.25h-8c-0.414 0-0.75 0.336-0.75 0.75v0 14c0.003 0.067 0.005 0.145 0.005 0.224 0 2.779-2.253 5.031-5.031 5.031-0.079 0-0.157-0.002-0.235-0.005l0.011 0c-0.067 0.003-0.145 0.005-0.223 0.005-2.779 0-5.032-2.253-5.032-5.032 0-0.079 0.002-0.157 0.005-0.234l-0 0.011v-14c-0-0.414-0.336-0.75-0.75-0.75h-8c-0.414 0-0.75 0.336-0.75 0.75v0 14c-0.009 0.187-0.014 0.407-0.014 0.628 0 7.807 6.329 14.136 14.136 14.136 0.221 0 0.44-0.005 0.659-0.015l-0.031 0.001c0.187 0.009 0.407 0.014 0.627 0.014 7.808 0 14.137-6.329 14.137-14.137 0-0.221-0.005-0.44-0.015-0.658l0.001 0.031z"/>
</svg>`;
                                    }, 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;
                location.reload();
            }
        });
        urlObserver.observe(document, { subtree: true, childList: true });

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

    initialize();
})();