豆瓣+TMDB影视工具(图片去重与全展示优化版)

影视信息提取与排版工具,优化图片重复与剧照展示(海报滚轮分页加载优化)

// ==UserScript==
// @name         豆瓣+TMDB影视工具(图片去重与全展示优化版)
// @namespace    tampermonkey
// @version      24.85
// @description  影视信息提取与排版工具,优化图片重复与剧照展示(海报滚轮分页加载优化)
// @author       豆包
// @match        https://pan1.me/?thread-create-*.htm
// @grant        GM_xmlhttpRequest
// @grant        GM_setClipboard
// @grant        GM_log
// @run-at       document-end
// @license      MIT  // 新增:声明MIT许可证
// ==/UserScript==

(function () {
    'use strict';

    // 核心配置
    const TMDB_CONFIG = {
        API_KEY: '860ffcf671f5c664bbadeb5a972f9650',
        ACCESS_TOKEN: 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiI4NjBmZmNmNjcxZjVjNjY0YmJhZGViNWE5NzJmOTY1MCIsIm5iZiI6MTc1NjE3NDMxMS40NDIsInN1YiI6IjY4YWQxN2U3YjYzZDI2MWNlNDQ0OTZlMiIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.XXHfRXpDp7S42rK5PdptQ6xGwtG5ohnXYTfdILwVxXg',
        BASE_URL: 'https://api.themoviedb.org/3',
        IMAGE_BASE_URL: 'https://image.tmdb.org/t/p/',
        POSTER_SIZE: 'w1280',
        STILL_SIZE: 'w1280',
        DOUBAN_QUALITY: {
            PRIORITY: ['raw', 'l', 'm'],
            TIMEOUT: 3000,
            RETRY: 1
        },
        IMAGE_CANDIDATES_COUNT: 5, // 每组加载数量(海报、剧照通用)
        POSTER_PER_ROW: 5,
        STILL_PER_ROW: 5
    };

    const COMMON_HEADERS = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1'
    };

    // 存储变量
    let selectedPosterUrl = '';
    let selectedStillUrl = '';
    let currentMovieInfo = null;
    let currentComments = [];
    let sourceCodeElement = null;
    let panelObserver = null;
    let isPanelInitialized = false;
    let currentEditor = null;
    let posterPage = 0;
    let isLoadingPosters = false;
    let isLoadingStills = false;
    let posterContainer = null;
    let stillContainer = null;
    let panel = null;
    let loadedPosterIds = new Set(); // 海报去重:记录已加载的海报唯一ID
    let stillPage = 0; // 剧照分页标记
    let loadedStillIds = new Set(); // 剧照去重:记录已加载的剧照唯一ID


    // 排版美化样式库
    const FORMAT_STYLES = [
        {
            name: '主标题',
            icon: 'fa-header',
            tag: 'h1',
            category: '标题',
            styles: {
                'color': '#1e40af',
                'font-size': '24px',
                'font-weight': 'bold',
                'margin': '20px 0 15px 0',
                'padding-bottom': '8px',
                'border-bottom': '2px solid #dbeafe'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '主标题示例';
                return `<h1 style="color:#1e40af;font-size:24px;font-weight:bold;margin:20px 0 15px 0;padding-bottom:8px;border-bottom:2px solid #dbeafe;">${content}</h1>`;
            }
        },
        {
            name: '副标题',
            icon: 'fa-header',
            tag: 'h2',
            category: '标题',
            styles: {
                'color': '#2563eb',
                'font-size': '20px',
                'font-weight': 'bold',
                'margin': '18px 0 12px 0',
                'padding-bottom': '5px',
                'border-bottom': '1px solid #dbeafe'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '副标题示例';
                return `<h2 style="color:#2563eb;font-size:20px;font-weight:bold;margin:18px 0 12px 0;padding-bottom:5px;border-bottom:1px solid #dbeafe;">${content}</h2>`;
            }
        },
        {
            name: '三级标题',
            icon: 'fa-header',
            tag: 'h3',
            category: '标题',
            styles: {
                'color': '#3b82f6',
                'font-size': '18px',
                'font-weight': 'bold',
                'margin': '15px 0 10px 0'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '三级标题示例';
                return `<h3 style="color:#3b82f6;font-size:18px;font-weight:bold;margin:15px 0 10px 0;">${content}</h3>`;
            }
        },
        {
            name: '正文段落',
            icon: 'fa-paragraph',
            tag: 'p',
            category: '文本',
            styles: {
                'color': '#333',
                'font-size': '14px',
                'line-height': '1.8',
                'margin': '8px 0',
                'text-indent': '2em'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '这是一段正文示例,包含标准的段落格式和首行缩进,适合用于大部分内容的展示。';
                return `<p style="color:#333;font-size:14px;line-height:1.8;margin:8px 0;text-indent:2em;">${content}</p>`;
            }
        },
        {
            name: '引用文本',
            icon: 'fa-quote-right',
            tag: 'blockquote',
            category: '文本',
            styles: {
                'color': '#666',
                'font-size': '13px',
                'line-height': '1.6',
                'margin': '10px 0',
                'padding': '10px 15px',
                'border-left': '3px solid #2196F3',
                'background': '#f8f9fa'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '这是一段引用文本示例,通常用于引用他人的话语或特殊说明内容。';
                return `<blockquote style="color:#666;font-size:13px;line-height:1.6;margin:10px 0;padding:10px 15px;border-left:3px solid #2196F3;background:#f8f9fa;">${content}</blockquote>`;
            }
        },
        {
            name: '无序列表',
            icon: 'fa-list-ul',
            tag: 'ul',
            category: '列表',
            styles: {
                'margin': '10px 0 10px 20px',
                'padding': '0'
            },
            itemStyles: {
                'color': '#444',
                'font-size': '14px',
                'line-height': '1.7',
                'margin': '5px 0',
                'list-style-type': 'disc'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '列表项1\n列表项2\n列表项3';
                const items = content.split('\n').map(item =>
                    `<li style="color:#444;font-size:14px;line-height:1.7;margin:5px 0;list-style-type:disc;">${item.trim()}</li>`
                ).join('');
                return `<ul style="margin:10px 0 10px 20px;padding:0;">${items}</ul>`;
            }
        },
        {
            name: '有序列表',
            icon: 'fa-list-ol',
            tag: 'ol',
            category: '列表',
            styles: {
                'margin': '10px 0 10px 20px',
                'padding': '0'
            },
            itemStyles: {
                'color': '#444',
                'font-size': '14px',
                'line-height': '1.7',
                'margin': '5px 0'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '步骤一\n步骤二\n步骤三';
                const items = content.split('\n').map(item =>
                    `<li style="color:#444;font-size:14px;line-height:1.7;margin:5px 0;">${item.trim()}</li>`
                ).join('');
                return `<ol style="margin:10px 0 10px 20px;padding:0;">${items}</ol>`;
            }
        },
        {
            name: '分隔线',
            icon: 'fa-minus',
            tag: 'hr',
            category: '布局',
            styles: {
                'border': 'none',
                'border-top': '1px solid #e0e0e0',
                'margin': '20px 0',
                'height': '1px'
            },
            preview: true,
            apply: () => {
                return `<hr style="border:none;border-top:1px solid #e0e0e0;margin:20px 0;height:1px;">`;
            }
        },
        {
            name: '高亮文本',
            icon: 'fa-highlighter',
            tag: 'span',
            category: '文本',
            styles: {
                'background': '#fff380',
                'padding': '0 3px',
                'border-radius': '2px'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '需要高亮的文本';
                return `<span style="background:#fff380;padding:0 3px;border-radius:2px;">${content}</span>`;
            }
        },
        {
            name: '链接样式',
            icon: 'fa-link',
            tag: 'a',
            category: '文本',
            styles: {
                'color': '#2563eb',
                'text-decoration': 'none',
                'border-bottom': '1px dashed #93c5fd',
                'padding': '0 1px'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '链接文本';
                return `<a href="#" style="color:#2563eb;text-decoration:none;border-bottom:1px dashed #93c5fd;padding:0 1px;">${content}</a>`;
            }
        },
        {
            name: '居中文本',
            icon: 'fa-align-center',
            tag: 'div',
            category: '布局',
            styles: {
                'text-align': 'center',
                'margin': '10px 0',
                'color': '#4b5563'
            },
            preview: true,
            apply: (selectedText) => {
                const content = selectedText || '这段文本会居中显示';
                return `<div style="text-align:center;margin:10px 0;color:#4b5563;">${content}</div>`;
            }
        },
        {
            name: '影视卡片',
            icon: 'fa-film',
            tag: 'div',
            category: '特殊',
            preview: true,
            apply: (selectedText) => {
                const title = selectedText || '影视名称';
                return `
<div style="border:1px solid #e5e7eb;border-radius:6px;padding:15px;margin:15px 0;box-shadow:0 1px 3px rgba(0,0,0,0.05);">
  <h3 style="margin-top:0;color:#1e40af;">${title}</h3>
  <p style="margin-bottom:0;color:#4b5563;font-size:14px;">这里可以添加影视的简要说明或推荐理由...</p>
</div>
                `;
            }
        }
    ];

    // 去重工具函数:过滤重复的图片URL
    function removeDuplicateUrls(urls) {
        return [...new Set(urls)].filter(url => url.trim() !== '');
    }

    // 自动填充和保存相关函数
    function autoClickSourceBtn() {
        return new Promise((resolve) => {
            const modalSourceBtn = document.querySelector('#myModal-code .btn, #source-code-btn');
            if (modalSourceBtn && modalSourceBtn.textContent.includes('源代码')) {
                modalSourceBtn.click();
                setTimeout(() => resolve(true), 600);
                return;
            }

            const textButtons = [...document.querySelectorAll('button'), ...document.querySelectorAll('a')]
                .filter(elem => elem.textContent.trim().includes('源代码'));
            if (textButtons.length > 0) {
                textButtons[0].click();
                setTimeout(() => resolve(true), 300);
                return;
            }

            const tinyMceBtn = document.querySelector('.tox-tbtn[title="源代码"]');
            const oldTinyMceBtn = Array.from(document.querySelectorAll('.mce-btn')).find(elem => elem.textContent.includes('源代码'));
            const ckSourceLabel = document.querySelector('.cke_button__source_label');

            if (tinyMceBtn) {
                tinyMceBtn.click();
                setTimeout(() => resolve(true), 300);
            } else if (oldTinyMceBtn) {
                oldTinyMceBtn.click();
                setTimeout(() => resolve(true), 300);
            } else if (ckSourceLabel && ckSourceLabel.closest('.cke_button')) {
                ckSourceLabel.closest('.cke_button').click();
                setTimeout(() => resolve(true), 300);
            } else {
                resolve(true);
            }
        });
    }

    function autoFillSourceBox(html) {
        return new Promise((resolve) => {
            let retryCount = 0;
            const maxRetry = 20;
            const interval = 300;

            const tryFill = setInterval(() => {
                retryCount++;
                const editorSelectors = [
                    '#myModal-code textarea',
                    'textarea.tox-textarea',
                    'textarea.mce-textbox',
                    'textarea.cke_source',
                    'textarea[name="message"]',
                    'textarea[name="content"]',
                    '#editor_content',
                    '#content',
                    'textarea[rows="20"][cols="80"]',
                    '.CodeMirror textarea',
                    '.editor-textarea'
                ];

                let targetBox = null;
                for (const selector of editorSelectors) {
                    const elem = document.querySelector(selector);
                    if (elem && elem.style.display !== 'none' && elem.offsetParent !== null) {
                        targetBox = elem;
                        sourceCodeElement = elem;
                        currentEditor = getCurrentEditor();
                        break;
                    }
                }

                if (targetBox) {
                    const codeMirror = targetBox.closest('.CodeMirror');
                    if (codeMirror && codeMirror.CodeMirror) {
                        codeMirror.CodeMirror.setValue(html);
                        codeMirror.CodeMirror.getDoc().markClean();
                        codeMirror.CodeMirror.getDoc().changed();
                    } else {
                        targetBox.value = html;
                        const inputEvent = new Event('input', { bubbles: true, cancelable: true });
                        const changeEvent = new Event('change', { bubbles: true, cancelable: true });
                        const blurEvent = new Event('blur', { bubbles: true, cancelable: true });

                        targetBox.dispatchEvent(inputEvent);
                        targetBox.dispatchEvent(changeEvent);
                        targetBox.dispatchEvent(blurEvent);
                        targetBox.focus();
                        targetBox.setSelectionRange(html.length, html.length);
                    }
                    clearInterval(tryFill);
                    resolve(true);
                    return;
                }

                if (retryCount >= maxRetry) {
                    clearInterval(tryFill);
                    resolve(false);
                }
            }, interval);
        });
    }

    function autoClickSaveBtn() {
        return new Promise((resolve) => {
            const saveButtons = [
                ...document.querySelectorAll('button'),
                ...document.querySelectorAll('a')
            ].filter(elem => {
                const text = elem.textContent.trim();
                return text === '保存' || text === '保存草稿' || text === '保存内容';
            });

            if (saveButtons.length > 0) {
                saveButtons[0].click();
                setTimeout(() => resolve(true), 500);
                return;
            }

            const commonSaveBtn = document.querySelector('button.save, .save-button, [type="submit"][value="保存"]');
            if (commonSaveBtn && !commonSaveBtn.textContent.includes('发布')) {
                commonSaveBtn.click();
                setTimeout(() => resolve(true), 500);
                return;
            }

            resolve(false);
        });
    }

    function autoFillTitleInput(title) {
        return new Promise((resolve) => {
            const titleInput = document.querySelector('input[placeholder="标题"], input[name="title"], #title');
            if (!titleInput) {
                resolve(false);
                return;
            }
            titleInput.value = title || (currentMovieInfo?.title || '影视内容分享');
            titleInput.dispatchEvent(new Event('input', { bubbles: true }));
            titleInput.dispatchEvent(new Event('change', { bubbles: true }));
            // 模拟键盘事件确保内容被检测
            const keydownEvent = new KeyboardEvent('keydown', { key: 'Enter', bubbles: true });
            const keyupEvent = new KeyboardEvent('keyup', { key: 'Enter', bubbles: true });
            titleInput.dispatchEvent(keydownEvent);
            titleInput.dispatchEvent(keyupEvent);
            resolve(true);
        });
    }

    async function fillAndSaveSource(html) {
        // 强制填充标题,解决标题缺失提示
        await autoFillTitleInput(currentMovieInfo?.title);

        showStatus('正在切换到源代码模式...', false);
        const switched = await autoClickSourceBtn();

        if (!switched) {
            showStatus('未检测到源代码按钮,尝试直接填充...', false);
        }

        showStatus('正在填充内容到编辑框...', false);
        const filled = await autoFillSourceBox(html);

        if (filled) {
            showStatus('内容填充完成,正在自动保存编辑内容(非发布)...', false);
            const saved = await autoClickSaveBtn();

            if (saved) {
                showStatus('内容已填充并自动保存(非发布)', false);
            } else {
                showStatus('内容已填充,未找到“保存”按钮,请手动点击保存', false);
            }
            return true;
        } else {
            GM_setClipboard(html);
            showStatus('自动填充失败,内容已复制到剪贴板,请手动粘贴', true);
            const pasteBtn = document.getElementById('paste-btn');
            if (pasteBtn) pasteBtn.style.display = 'inline-block';
            return false;
        }
    }

    // 内容生成函数
    function generateHTML(movie, comments, posterDataURL, stillDataURL) {
        const finalStillUrl = stillDataURL || 'https://picsum.photos/800/450?default-still';
        const runtime = movie.runtime === 'null' || !movie.runtime ? '未知片长' : movie.runtime;

        let imdbHtml = '';
        if (movie.imdbId && movie.imdbId !== '暂无') {
            imdbHtml = `<span>&nbsp;</span><strong style="box-sizing: border-box; font-weight: bolder;">IMDb:</strong><span style="box-sizing: border-box; color: rgb(0, 2, 255);"><a style="box-sizing: border-box; color: rgb(0, 2, 255); text-decoration: none; background-color: transparent; transition: 0.2s;" href="https://www.imdb.com/title/${movie.imdbId}/" target="_blank" rel="noopener"><span>&nbsp;</span>${movie.imdbId}</a></span>`;
        }

        const introHtml = movie.intro
            .split('\n')
            .filter(para => para.trim())
            .map(para => `<div style="box-sizing: border-box; color: rgb(33, 37, 41); font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">${para.trim()}</div>`)
            .join('');

        let commentsHtml = '';
        if (comments && comments.length > 0) {
            commentsHtml = `
<h3 style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0.5rem; font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-weight: 500; line-height: 1.2; color: rgb(33, 37, 41); font-size: 1.75rem; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">影视热评:</h3>
<div style="box-sizing: border-box; color: rgb(33, 37, 41); font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">${comments[0]}</div>
            `;
        }

        return `
<div class="card border" style="box-sizing: border-box; position: relative; display: flex; flex-direction: column; min-width: 0px; overflow-wrap: break-word; background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border: none; border-radius: 0.75rem; margin-bottom: 1rem; box-shadow: rgba(46, 45, 116, 0.05) 0px 0.25rem 1.875rem; color: rgb(33, 37, 41); font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<div class="movie-info" style="box-sizing: border-box; flex: 1 1 400px; padding: 20px;"><img class="lazy img-responsive" style="box-sizing: border-box; vertical-align: middle; border: 1px solid transparent; max-width: 100%; -webkit-user-drag: none; margin-bottom: 0.5rem; height: auto; width: 816.818px; cursor: pointer;" src="${posterDataURL}" width="1080" height="1522" data-original="${posterDataURL}"><br style="box-sizing: border-box;">
<div class="movie-info-content" style="box-sizing: border-box;">
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">名称:</strong>${movie.title}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">又名:</strong>${movie.alsoKnown || '无'}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">导演:</strong>${movie.director}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">编剧:</strong>${movie.writer}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">主演:</strong>${movie.actor}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">类型:</strong>${movie.genreTags.join('、') || '未知'}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">制片地区:</strong>${movie.region}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">上映时间:</strong>${movie.release}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">影视语言:</strong>${movie.lang}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">评分:</strong><span style="box-sizing: border-box; color: rgb(0, 132, 255); font-weight: 600;"><span>&nbsp;</span>${movie.rating}</span><span>&nbsp;</span><strong style="box-sizing: border-box; font-weight: bolder;">豆瓣ID:</strong><span style="box-sizing: border-box; color: rgb(0, 2, 255);"><a style="box-sizing: border-box; color: rgb(0, 2, 255); text-decoration: none; background-color: transparent; transition: 0.2s;" href="${movie.mediaType === 'tv' ? 'https://tv.douban.com/subject/' : 'https://movie.douban.com/subject/'}${movie.doubanId || movie.tmdbId}/" target="_blank" rel="noopener"><span>&nbsp;</span>${movie.doubanId || movie.tmdbId}</a></span>${imdbHtml}</p>
<p style="box-sizing: border-box; margin: 0.2rem 0px; line-height: 1.7;"><strong style="box-sizing: border-box; font-weight: bolder;">片长:</strong>${runtime}</p>
</div>
</div>
</div>
<h3 style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0.5rem; font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-weight: 500; line-height: 1.2; color: rgb(33, 37, 41); font-size: 1.75rem; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">影视简介:</h3>
${introHtml}
<div style="box-sizing: border-box; color: rgb(33, 37, 41); font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">&nbsp;</div>
<div style="box-sizing: border-box; color: rgb(33, 37, 41); font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<h3 style="box-sizing: border-box; margin-top: 0px; margin-bottom: 0.5rem; font-family: 'Helvetica Neue', Helvetica, 'Microsoft Yahei', 'Hiragino Sans GB', 'WenQuanYi Micro Hei', 微软雅黑, 华文细黑, STHeiti, sans-serif; font-weight: 500; line-height: 1.2; color: rgb(33, 37, 41); font-size: 1.75rem; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">精彩剧照:</h3>
<img src="${finalStillUrl}" style="box-sizing: border-box; vertical-align: middle; border-style: none; max-width: 100%; height: auto; border-radius: 4px; margin-bottom: 1rem;" alt="${movie.title} 剧照">
</div>
${commentsHtml}
        `;
    }

    // 控制面板创建与定位 - 精准放置到标记位置
    function createPanel() {
        panel = document.createElement('div');
        panel.id = 'douban-tmdb-panel';
        panel.style.cssText = `
        background: #fff; border: 1px solid #e5e7eb; border-radius: 6px;
        padding: 10px; margin: 8px 0; box-shadow: 0 1px 2px rgba(0,0,0,0.05);
        z-index: 999; position: relative;
        box-sizing: border-box;
        width: 100%; max-width: 1000px;
    `;
        panel.innerHTML = `
        <div style="margin:0 0 10px 0; font-size:16px; font-weight:600; color:#374151; border-bottom:1px solid #e5e7eb; padding-bottom:6px;">豆瓣+TMDB影视工具</div>

        <!-- 排版美化工具区域 -->
        <div style="margin-bottom:12px; padding:8px; background:#f9fafb; border-radius:4px;">
            <h4 style="margin:0 0 8px 0; color:#4b5563; font-size:14px; display:flex; justify-content:space-between; align-items:center;">
                排版美化工具
                <span id="format-preview-toggle" style="font-size:12px; color:#3b82f6; cursor:pointer; text-decoration:underline;">显示预览</span>
            </h4>

            <!-- 样式分类标签 -->
            <div id="format-categories" style="display:flex; gap:8px; margin-bottom:8px; overflow-x:auto; padding-bottom:4px; border-bottom:1px solid #e2e8f0;">
                <!-- 分类标签通过JS生成 -->
            </div>

            <!-- 样式按钮区域 -->
            <div style="display:flex; flex-wrap:wrap; gap:6px;" id="format-buttons">
                <!-- 美化按钮通过JS生成 -->
            </div>

            <!-- 样式预览区域 -->
            <div id="format-preview" style="margin-top:10px; padding:10px; background:white; border:1px solid #e5e7eb; border-radius:4px; display:none; max-height:200px; overflow-y:auto; font-family:'Microsoft YaHei', sans-serif;">
                <div style="text-align:center; color:#6b7280; font-size:13px;">选择样式查看预览效果</div>
            </div>
        </div>

        <div style="margin-bottom:10px; position:relative;">
            <label style="display:inline-block; width:70px; font-weight:500; color:#4b5563;">搜索影片:</label>
            <input type="text" id="search-movie" placeholder="直接输入名称(如:奥本海默)" style="width: calc(100% - 80px); padding:5px; border:1px solid #d1d5db; border-radius:4px; font-size:13px;">
            <div id="search-loading" style="position:absolute; right:10px; top:6px; color:#9ca3af; display:none;">
                <i>搜索中...</i>
            </div>
            <div id="search-results" style="position:absolute; z-index:1000; background:#fff; border:1px solid #d1d5db; border-radius:4px; max-height:300px; overflow-y:auto; width: calc(100% - 80px); left:70px; top: 32px; display:none;"></div>
        </div>

        <div style="margin-bottom:8px;">
            <label style="display:inline-block; width:70px; font-weight:500; color:#4b5563;">影视链接:</label>
            <input type="text" id="media-url" placeholder="豆瓣或TMDB链接" style="width: calc(100% - 80px); padding:5px; border:1px solid #d1d5db; border-radius:4px; font-size:13px;">
        </div>
        <div style="margin-bottom:8px; display:flex; align-items:center;">
            <button id="fetch-btn" style="background:#3b82f6; color:white; border:none; padding:6px 14px; border-radius:4px; cursor:pointer; font-size:13px; display:none;">提取并填充</button>
            <button id="paste-btn" style="background:#8b5cf6; color:white; border:none; padding:6px 14px; border-radius:4px; cursor:pointer; font-size:13px; display:none; margin-left:8px;">手动复制内容</button>
            <textarea id="backup-html" style="display:none;"></textarea>
        </div>

        <!-- 选择海报区域(Grid 布局 + 滚动条) -->
            <div id="image-selection" style="margin-top:12px; display:none;">
            <h4 style="color:#3b82f6; margin-bottom:8px; font-size:14px;">选择海报(点击图片选择):</h4>
            <div id="poster-candidates" style="display: grid; grid-template-columns: repeat(5, 1fr); gap:8px; padding:8px; margin-bottom:10px; border:1px solid #e5e7eb; border-radius:4px; min-height:200px; max-height:400px; overflow-y:auto;"></div>
            <button id="load-more-posters" style="display: none; background:#60a5fa; color:white; border:none; padding:4px 10px; border-radius:3px; cursor:pointer; font-size:12px; margin-bottom:15px;">加载更多海报</button>

            <h4 style="color:#3b82f6; margin-bottom:8px; font-size:14px;">选择剧照(点击图片选择):</h4>
            <div id="still-candidates" style="display: none; grid-template-columns: repeat(5, 1fr); gap:8px; padding:8px; margin-bottom:10px; border:1px solid #e5e7eb; border-radius:4px; min-height:200px; max-height:400px; overflow-y:auto;"></div>
            <button id="load-more-stills" style="display: none; background:#60a5fa; color:white; border:none; padding:4px 10px; border-radius:3px; cursor:pointer; font-size:12px; margin-bottom:15px;">加载更多剧照</button>

            <!-- 清除按钮与确认按钮同组 -->
            <div style="display:flex; align-items:center; margin-top:10px;">
                <button id="clear-btn" style="background:#ef4444; color:white; border:none; padding:6px 14px; border-radius:4px; cursor:pointer; font-size:13px; margin-right:8px;">清除</button>
                <button id="confirm-images-btn" style="background:#10b981; color:white; border:none; padding:6px 16px; border-radius:4px; cursor:pointer; font-size:13px;">确认选择并填充(自动保存编辑内容)</button>
            </div>
        </div>

        <div id="status" style="margin-top:8px; padding:6px; border-radius:4px; font-size:12px; display:none;"></div>
    `;
        return panel;
    }


    // 插入面板到图片标记的固定位置(精准定位)
    function insertPanelInMarkedPosition() {
        if (isPanelInitialized) return;

        // 创建面板
        panel = createPanel();

        // 扩展目标容器选择器 + 空值安全处理
        const targetContainers = [
            // 优先匹配内容区域主容器
            document.querySelector('.main-content'),
            // 对可能为 null 的选择器进行空值包装
            (document.querySelector('#thread-create-form') || {}).parentElement,
            (document.querySelector('.post-form') || {}).parentElement,
            document.querySelector('.panel-default'),
            document.body // 最终 fallback 到 body
        ];

        let targetContainer = null;
        // 遍历找到第一个有效容器
        for (const container of targetContainers) {
            if (container && container.offsetParent !== null) {
                targetContainer = container;
                break;
            }
        }

        // 确保总有有效容器(兜底)
        if (!targetContainer) {
            targetContainer = document.body;
            console.warn('无法找到目标容器,已 fallback 到 document.body');
        }

        // 移除旧面板(若存在)
        const oldPanel = targetContainer.querySelector('#douban-tmdb-panel');
        if (oldPanel) targetContainer.removeChild(oldPanel);

        // 插入新面板到容器顶部
        if (targetContainer.firstChild) {
            targetContainer.insertBefore(panel, targetContainer.firstChild);
        } else {
            targetContainer.appendChild(panel);
        }

        // 初始化组件
        posterContainer = document.getElementById('poster-candidates');
        stillContainer = document.getElementById('still-candidates');
        initFormatTools();
        bindEventListeners();
        setupMutationObserver(targetContainer);
        isPanelInitialized = true;
        showStatus('控制面板已放置在可用位置', false);
        return true;
    }
    insertPanelInMarkedPosition();

    // 监听父容器变化,确保面板位置不移动
    function setupMutationObserver(parent) {
        if (panelObserver) {
            panelObserver.disconnect();
        }

        panelObserver = new MutationObserver((mutations) => {
            mutations.forEach(mutation => {
                const panel = document.getElementById('douban-tmdb-panel');
                // 确保面板始终在父容器内,不被移动
                if (!panel || !parent.contains(panel)) {
                    if (parent.firstChild) {
                        parent.insertBefore(panel || createPanel(), parent.firstChild);
                    } else {
                        parent.appendChild(panel || createPanel());
                    }
                    // 重新初始化以保持功能完整
                    posterContainer = document.getElementById('poster-candidates');
                    stillContainer = document.getElementById('still-candidates');
                    initFormatTools();
                    bindEventListeners();
                }
            });
        });

        panelObserver.observe(parent, {
            childList: true,
            attributes: true,
            subtree: true,
            characterData: true
        });
    }

    // 工具函数
    function showStatus(text, isError = false) {
        const status = document.getElementById('status');
        if (!status) return;
        status.textContent = text;
        status.style.display = 'block';
        status.style.cssText = `
            margin-top:8px; padding:6px; border-radius:4px; font-size:12px;
            ${isError ? 'background:#fee2e2; color:#b91c1c; border:1px solid #fecaca;' : 'background:#ecfdf5; color:#065f46; border:1px solid #d1fae5;'}
        `;
    }

    function debounce(func, wait) {
        let timeout;
        return function () {
            const context = this;
            const args = arguments;
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(context, args), wait);
        };
    }

    function safeGet(obj, path, defaultValue = '') {
        try {
            return path.split('.').reduce((o, k) => o[k], obj) || defaultValue;
        } catch (e) {
            return defaultValue;
        }
    }

    // 图片获取函数
    function getImageDataURLWithQuality(url) {
        return new Promise((resolve) => {
            if (!url) {
                resolve('https://picsum.photos/800/450?default-still');
                return;
            }
            let baseUrl = url;
            if (baseUrl.includes('doubanio.com') && !baseUrl.includes('https:')) {
                baseUrl = 'https:' + baseUrl;
            }
            if (baseUrl.includes('doubanio.com') && baseUrl.includes('/m/')) {
                const qualityUrls = TMDB_CONFIG.DOUBAN_QUALITY.PRIORITY.map(quality =>
                    baseUrl.replace('/m/', `/${quality}/`)
                );
                const tryQuality = (index, retryCount = 0) => {
                    if (index >= qualityUrls.length) {
                        getFallbackImageDataURL(baseUrl).then(resolve);
                        return;
                    }
                    const currentUrl = qualityUrls[index];
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: currentUrl,
                        headers: { ...COMMON_HEADERS, 'Referer': 'https://movie.douban.com/' },
                        responseType: 'blob',
                        timeout: TMDB_CONFIG.DOUBAN_QUALITY.TIMEOUT,
                        onload: (res) => {
                            if (res.status === 200 && res.response) {
                                const reader = new FileReader();
                                reader.onload = (e) => resolve(e.target.result);
                                reader.readAsDataURL(res.response);
                            } else if (retryCount < TMDB_CONFIG.DOUBAN_QUALITY.RETRY) {
                                setTimeout(() => tryQuality(index, retryCount + 1), 500);
                            } else {
                                tryQuality(index + 1);
                            }
                        },
                        onerror: () => {
                            if (retryCount < TMDB_CONFIG.DOUBAN_QUALITY.RETRY) {
                                setTimeout(() => tryQuality(index, retryCount + 1), 500);
                            } else {
                                tryQuality(index + 1);
                            }
                        },
                        ontimeout: () => {
                            if (retryCount < TMDB_CONFIG.DOUBAN_QUALITY.RETRY) {
                                setTimeout(() => tryQuality(index, retryCount + 1), 500);
                            } else {
                                tryQuality(index + 1);
                            }
                        }
                    });
                };
                tryQuality(0);
                return;
            }
            getFallbackImageDataURL(baseUrl).then(resolve);
        });
    }

    function getFallbackImageDataURL(url) {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: {
                    ...COMMON_HEADERS,
                    'Referer': url.includes('doubanio.com') ? 'https://movie.douban.com/' :
                        url.includes('themoviedb.org') ? 'https://www.themoviedb.org/' : ''
                },
                responseType: 'blob',
                timeout: 5000,
                onload: (res) => {
                    if (res.status === 200 && res.response) {
                        const reader = new FileReader();
                        reader.onload = (e) => resolve(e.target.result);
                        reader.readAsDataURL(res.response);
                    } else {
                        resolve('https://picsum.photos/800/450?error-still');
                    }
                },
                onerror: () => resolve('https://picsum.photos/800/450?error-still'),
                ontimeout: () => resolve('https://picsum.photos/800/450?error-still')
            });
        });
    }

    // 确保URL有效性,保障加载更多功能(去重处理)
    async function getDoubanOfficialPosters(subjectUrl) {
        return new Promise(resolve => {
            try {
                const urlObj = new URL(subjectUrl);
                // 访问豆瓣“全部图片”页面(type=R),获取更多海报
                const photosUrl = subjectUrl.replace(/\/subject\/(\d+)\/?$/, '/subject/$1/photos?type=R');
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: photosUrl,
                    headers: { ...COMMON_HEADERS, 'Referer': subjectUrl, 'Host': urlObj.hostname },
                    timeout: 8000,
                    onload: (res) => {
                        try {
                            const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                            // 提取所有海报图片(含多页),并替换为高质量版本(m→l)
                            const posterImgs = Array.from(doc.querySelectorAll('.poster-col3 li img'))
                                .map(img => {
                                    let src = img.src;
                                    if (src.includes('/m/')) src = src.replace('/m/', '/l/'); // 低质量→高质量
                                    return { url: src, id: src }; // 返回带id的对象(用url作为唯一标识)
                                })
                                .filter(Boolean);
                            // 去重处理(通过id)
                            const uniquePosters = [];
                            const idSet = new Set();
                            posterImgs.forEach(img => {
                                if (!idSet.has(img.id)) {
                                    idSet.add(img.id);
                                    uniquePosters.push(img);
                                }
                            });
                            resolve(uniquePosters.map(img => img.url)); // 仍返回url数组,保持原有逻辑兼容
                        } catch (e) {
                            console.error('解析豆瓣海报页面失败:', e);
                            resolve([]);
                        }
                    },
                    onerror: () => {
                        console.error('获取豆瓣海报请求错误');
                        resolve([]);
                    },
                    ontimeout: () => {
                        console.error('获取豆瓣海报请求超时');
                        resolve([]);
                    }
                });
            } catch (e) {
                console.error('无效的豆瓣主题URL:', subjectUrl, e);
                resolve([]);
            }
        });
    }

    // 确保URL有效性,保障剧照展示(去重处理)
    function getDoubanStillsList(url) {
        return new Promise(resolve => {
            try {
                const urlObj = new URL(url);
                const stillsUrl = url.replace(/\/subject\/(\d+)\/?$/, '/subject/$1/photos?type=still');
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: stillsUrl,
                    headers: { ...COMMON_HEADERS, 'Referer': url, 'Host': urlObj.hostname },
                    timeout: 8000,
                    onload: (res) => {
                        try {
                            const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                            const labeledStills = Array.from(doc.querySelectorAll('.poster-col3 li img[data-title*="剧照"]')).map(img => img.src).filter(Boolean);
                            const finalStills = labeledStills.length ? labeledStills : Array.from(doc.querySelectorAll('.poster-col3 li img')).map(img => img.src).filter(Boolean);
                            // 去重处理
                            const uniqueStills = removeDuplicateUrls(finalStills);
                            resolve(uniqueStills);
                        } catch (e) {
                            console.error('解析豆瓣剧照页面失败:', e);
                            resolve([]);
                        }
                    },
                    onerror: () => {
                        console.error('获取豆瓣剧照请求错误');
                        resolve([]);
                    },
                    ontimeout: () => {
                        console.error('获取豆瓣剧照请求超时');
                        resolve([]);
                    }
                });
            } catch (e) {
                console.error('无效的豆瓣URL:', url, e);
                resolve([]);
            }
        });
    }

    function getTMDBStillsList(mediaType, id) {
        return new Promise(resolve => {
            const stillCutsUrl = `${TMDB_CONFIG.BASE_URL}/${mediaType}/${id}/images?api_key=${TMDB_CONFIG.API_KEY}&include_image_language=zh,en&image_type=still_cuts&sort_by=primary`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: stillCutsUrl,
                headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                timeout: 10000,
                onload: (res) => {
                    try {
                        const data = JSON.parse(res.responseText);
                        const stillCuts = safeGet(data, 'still_cuts', []);
                        let stillUrls = stillCuts.map(img => `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.STILL_SIZE}/${img.file_path}`).filter(Boolean);
                        if (stillUrls.length >= TMDB_CONFIG.IMAGE_CANDIDATES_COUNT) {
                            // 去重处理
                            stillUrls = removeDuplicateUrls(stillUrls);
                            resolve(stillUrls);
                            return;
                        }
                        const backdropsUrl = `${TMDB_CONFIG.BASE_URL}/${mediaType}/${id}/images?api_key=${TMDB_CONFIG.API_KEY}&include_image_language=zh,en&image_type=backdrop&sort_by=vote_average.desc`;
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: backdropsUrl,
                            headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                            onload: (res) => {
                                try {
                                    const data = JSON.parse(res.responseText);
                                    const backdrops = safeGet(data, 'backdrops', []);
                                    const backdropUrls = backdrops.map(img => `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.STILL_SIZE}/${img.file_path}`).filter(Boolean);
                                    stillUrls = [...stillUrls, ...backdropUrls].slice(0, TMDB_CONFIG.IMAGE_CANDIDATES_COUNT);
                                    // 去重处理
                                    stillUrls = removeDuplicateUrls(stillUrls);
                                    if (stillUrls.length >= TMDB_CONFIG.IMAGE_CANDIDATES_COUNT) {
                                        resolve(stillUrls);
                                        return;
                                    }
                                    const postersUrl = `${TMDB_CONFIG.BASE_URL}/${mediaType}/${id}/images?api_key=${TMDB_CONFIG.API_KEY}&include_image_language=zh,en&image_type=poster&sort_by=primary`;
                                    GM_xmlhttpRequest({
                                        method: 'GET',
                                        url: postersUrl,
                                        headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                                        onload: (res) => {
                                            try {
                                                const data = JSON.parse(res.responseText);
                                                const posters = safeGet(data, 'posters', []);
                                                const posterUrls = posters.slice(1).map(img => `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.STILL_SIZE}/${img.file_path}`).filter(Boolean);
                                                stillUrls = [...stillUrls, ...posterUrls].slice(0, TMDB_CONFIG.IMAGE_CANDIDATES_COUNT);
                                                // 去重处理
                                                stillUrls = removeDuplicateUrls(stillUrls);
                                                resolve(stillUrls);
                                            } catch (e) {
                                                resolve(stillUrls);
                                            }
                                        },
                                        onerror: () => resolve(stillUrls),
                                        ontimeout: () => resolve(stillUrls)
                                    });
                                } catch (e) {
                                    resolve(stillUrls);
                                }
                            },
                            onerror: () => resolve(stillUrls),
                            ontimeout: () => resolve(stillUrls)
                        });
                    } catch (e) {
                        resolve([]);
                    }
                },
                onerror: () => resolve([]),
                ontimeout: () => resolve([])
            });
        });
    }

    // 搜索相关函数
    function searchDouban(query) {
        return new Promise((resolve) => {
            const url = `https://search.douban.com/movie/subject_search?search_text=${encodeURIComponent(query)}&cat=1002`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: { ...COMMON_HEADERS, 'Referer': 'https://movie.douban.com/', 'Host': 'search.douban.com' },
                timeout: 8000,
                onload: (res) => {
                    try {
                        const html = res.responseText;
                        const dataMatch = html.match(/window\.__DATA__\s*=\s*({.*?});/s);
                        if (!dataMatch || !dataMatch[1]) {
                            resolve([]);
                            return;
                        }
                        const cleanData = dataMatch[1].replace(/\\x([0-9A-Fa-f]{2})/g, (match, hex) =>
                            String.fromCharCode(parseInt(hex, 16))
                        );
                        const doubanData = JSON.parse(cleanData);
                        const items = safeGet(doubanData, 'items', []);
                        const results = items.map(item => ({
                            title: safeGet(item, 'title', '未知作品'),
                            type: safeGet(item, 'labels', []).some(l => l.text === '剧集') ? '电视剧' : '电影',
                            year: safeGet(item, 'title', '').match(/\((\d{4})\)/)?.[1] || '未知',
                            source: '豆瓣',
                            id: safeGet(item, 'id', ''),
                            url: safeGet(item, 'url', ''),
                            poster: safeGet(item, 'cover_url', '')
                        })).filter(item => item.url);
                        resolve(results);
                    } catch (e) {
                        resolve([]);
                    }
                },
                onerror: () => resolve([]),
                ontimeout: () => resolve([])
            });
        });
    }

    function searchTMDB(query) {
        return new Promise((resolve) => {
            const searchUrl = `${TMDB_CONFIG.BASE_URL}/search/multi?api_key=${TMDB_CONFIG.API_KEY}&query=${encodeURIComponent(query)}&language=zh-CN`;
            GM_xmlhttpRequest({
                method: 'GET',
                url: searchUrl,
                headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                timeout: 8000,
                onload: (res) => {
                    try {
                        const data = JSON.parse(res.responseText);
                        const results = safeGet(data, 'results', []).map(item => ({
                            title: item.title || item.name || '未知作品',
                            type: item.media_type === 'movie' ? '电影' : item.media_type === 'tv' ? '电视剧' : '未知',
                            year: item.release_date?.split('-')[0] || item.first_air_date?.split('-')[0] || '未知',
                            source: 'TMDB',
                            id: item.id || '',
                            url: item.media_type === 'movie'
                                ? `https://www.themoviedb.org/movie/${item.id}`
                                : item.media_type === 'tv'
                                    ? `https://www.themoviedb.org/tv/${item.id}`
                                    : '',
                            poster: item.poster_path
                                ? `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.POSTER_SIZE}/${item.poster_path}`
                                : ''
                        })).filter(item => item.url);
                        resolve(results);
                    } catch (e) {
                        resolve([]);
                    }
                },
                onerror: () => resolve([]),
                ontimeout: () => resolve([])
            });
        });
    }

    // 搜索结果交互
    function setupSearchInteractions() {
        const searchInput = document.getElementById('search-movie');
        const resultsContainer = document.getElementById('search-results');
        const loadingIndicator = document.getElementById('search-loading');
        const mediaUrlInput = document.getElementById('media-url');
        let lazyLoadController = new AbortController();
        let lastSearchQuery = '';
        let lastSearchResults = [];

        if (!searchInput || !resultsContainer || !loadingIndicator || !mediaUrlInput) return;

        searchInput.addEventListener('input', debounce(async function () {
            const query = this.value.trim();
            lastSearchQuery = query;
            if (!query) {
                resultsContainer.style.display = 'none';
                loadingIndicator.style.display = 'none';
                lastSearchResults = [];
                abortLazyLoad();
                return;
            }
            loadingIndicator.style.display = 'block';
            resultsContainer.style.display = 'none';
            abortLazyLoad();
            lazyLoadController = new AbortController();
            try {
                const searchPromise = Promise.all([searchDouban(query), searchTMDB(query)]);
                const [doubanResults, tmdbResults] = await Promise.race([
                    searchPromise,
                    new Promise(resolve => setTimeout(() => resolve([[], []]), 8000))
                ]);
                loadingIndicator.style.display = 'none';
                const allResults = [...doubanResults, ...tmdbResults];
                const uniqueResults = allResults.filter((item, index, self) =>
                    index === self.findIndex(t => t.title === item.title && t.year === item.year)
                );
                lastSearchResults = uniqueResults;
                if (uniqueResults.length === 0) {
                    resultsContainer.innerHTML = '<div style="padding:6px; color:#6b7280;">未找到结果,请尝试其他关键词</div>';
                    resultsContainer.style.display = 'block';
                    return;
                }
                let resultHtml = '<div class="results-container">';
                uniqueResults.forEach((item, index) => {
                    const isDouban = item.source === '豆瓣';
                    const isTMDB = item.source === 'TMDB';
                    resultHtml += `
                        <div class="search-item" data-url="${item.url}" data-type="${item.type}" data-index="${index}" style="padding:6px; cursor:pointer; border-bottom:1px solid #e5e7eb; display:flex; align-items:center; gap:6px; ${isDouban ? 'background:#eff6ff;' : isTMDB ? 'background:#e0f2fe;' : ''}">
                            <div class="poster-placeholder" style="width:36px; height:54px; background:#f3f4f6; border-radius:3px; display:flex; align-items:center; justify-content:center; color:#9ca3af;">
                                <i class="fa fa-film" style="font-size:14px;"></i>
                            </div>
                            <div>
                                <div><strong>${item.title}</strong> ${isDouban ? '<span style="background:#3b82f6; color:white; font-size:9px; padding:1px 2px; border-radius:2px;">豆瓣</span>' : isTMDB ? '<span style="background:#0ea5e9; color:white; font-size:9px; padding:1px 2px; border-radius:2px;">TMDB</span>' : ''}</div>
                                <div style="font-size:11px; color:#6b7280;">${item.type} · ${item.year} · ${item.source}</div>
                            </div>
                        </div>
                    `;
                });
                resultHtml += '</div>';
                resultsContainer.innerHTML = resultHtml;
                resultsContainer.style.display = 'block';
                const observer = new IntersectionObserver((entries) => {
                    entries.forEach(entry => {
                        if (entry.isIntersecting) {
                            const item = entry.target;
                            const index = parseInt(item.getAttribute('data-index'));
                            const resultItem = uniqueResults[index];
                            if (resultItem.poster && !lazyLoadController.signal.aborted) {
                                getImageDataURLWithQuality(resultItem.poster).then(dataUrl => {
                                    if (lazyLoadController.signal.aborted) return;
                                    const placeholder = item.querySelector('.poster-placeholder');
                                    if (placeholder) {
                                        placeholder.innerHTML = `<img src="${dataUrl}" style="width:36px; height:54px; object-fit:cover; border-radius:3px;" alt="${resultItem.title}">`;
                                    }
                                });
                            }
                            observer.unobserve(item);
                        }
                    });
                }, { rootMargin: '0px 0px 150px 0px' });
                document.querySelectorAll('.search-item').forEach(item => {
                    observer.observe(item);
                });
            } catch (e) {
                loadingIndicator.style.display = 'none';
                resultsContainer.innerHTML = '<div style="padding:6px; color:#6b7280;">搜索出错,请重试</div>';
                resultsContainer.style.display = 'block';
            }
        }, 500));

        function abortLazyLoad() {
            if (lazyLoadController) {
                lazyLoadController.abort();
            }
        }

        searchInput.addEventListener('blur', () => {
            const hideTimeout = setTimeout(() => {
                resultsContainer.style.display = 'none';
            }, 200);
            searchInput.dataset.hideTimeout = hideTimeout;
        });

        searchInput.addEventListener('focus', function () {
            const existingTimeout = this.dataset.hideTimeout;
            if (existingTimeout) {
                clearTimeout(existingTimeout);
                delete this.dataset.hideTimeout;
            }
            const query = this.value.trim();
            if (query) {
                resultsContainer.style.display = 'block';
                if (lastSearchResults.length === 0 || lastSearchQuery !== query) {
                    const inputEvent = new Event('input', { bubbles: true });
                    this.dispatchEvent(inputEvent);
                }
            }
        });

        resultsContainer.addEventListener('click', async function (event) {
            const targetItem = event.target.closest('.search-item');
            if (targetItem) {
                const url = targetItem.getAttribute('data-url');
                const type = targetItem.getAttribute('data-type');
                const title = targetItem.querySelector('strong').textContent;
                if (url) {
                    mediaUrlInput.value = url;
                    resultsContainer.style.display = 'none';
                    searchInput.blur();
                    const fetchBtn = document.getElementById('fetch-btn');
                    if (fetchBtn) fetchBtn.style.display = 'none';
                    showStatus(`正在加载【${type}】${title}的信息...`, false);
                    try {
                        currentMovieInfo = await getBasicInfo(url);
                        currentComments = await getHotComments(url);
                        showStatus('信息加载完成,请选择海报和剧照', false);
                        await showImageSelection(currentMovieInfo);
                    } catch (err) {
                        showStatus(`加载失败:${err.message}`, true);
                        if (mediaUrlInput.value.trim() && fetchBtn) {
                            fetchBtn.style.display = 'inline-block';
                        }
                    }
                }
            }
        });

        document.addEventListener('click', function (event) {
            if (!searchInput.contains(event.target) && !resultsContainer.contains(event.target)) {
                resultsContainer.style.display = 'none';
            }
        });
    }

    // 影视信息提取
    function getBasicInfo(url) {
        return new Promise((resolve, reject) => {
            const urlObj = new URL(url);
            if (url.includes('douban.com')) {
                let isTv = url.includes('tv.douban.com');
                const headers = {
                    ...COMMON_HEADERS,
                    'Referer': 'https://movie.douban.com/',
                    'Host': urlObj.hostname
                };
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: url,
                    headers: headers,
                    timeout: 10000,
                    onload: async (res) => {
                        try {
                            const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                            const titleElem = doc.querySelector('h1 span[property="v:itemreviewed"], h1 span[itemprop="name"]');
                            const title = titleElem ? titleElem.textContent.trim() : (isTv ? '未知电视剧' : '未知电影');
                            const genreTags = Array.from(doc.querySelectorAll('span[property="v:genre"]')).map(g => g.textContent.trim()).filter(Boolean);
                            let year = '未知';
                            const yearElem = doc.querySelector('span[property="v:initialReleaseDate"]');
                            if (yearElem) {
                                const yearMatch = yearElem.textContent.trim().match(/\d{4}/);
                                year = yearMatch ? yearMatch[0] : '未知';
                            }
                            const alsoKnown = doc.querySelector('span[property="v:alternative"]')?.textContent.trim() || '';
                            const director = Array.from(doc.querySelectorAll('a[rel="v:directedBy"]')).map(d => d.textContent.trim()).join(' / ') || '未知';
                            const writer = Array.from(doc.querySelectorAll('.attrs')[1]?.querySelectorAll('a') || []).map(w => w.textContent.trim()).join(' / ') || '未知';
                            const actor = Array.from(doc.querySelectorAll('a[rel="v:starring"]')).map(a => a.textContent.trim()).slice(0, 6).join(',') || '未知';
                            let region = '未知';
                            const infoLis = doc.querySelectorAll('#info li');
                            Array.from(infoLis).some(li => {
                                const text = li.textContent.trim();
                                if (text.startsWith('制片国家/地区') || text.startsWith('地区')) {
                                    region = text.split(/[::]/)[1]?.trim() || '未知';
                                    return true;
                                }
                                return false;
                            });
                            let lang = '未知';
                            Array.from(infoLis).some(li => {
                                const text = li.textContent.trim();
                                if (text.startsWith('语言')) {
                                    lang = text.split(/[::]/)[1]?.trim() || '未知';
                                    return true;
                                }
                                return false;
                            });
                            const release = yearElem?.textContent.trim() || (isTv ? '未知首播时间' : '未知上映时间');
                            const rating = doc.querySelector('strong[property="v:average"]')?.textContent || '暂无';
                            const doubanId = url.match(/subject\/(\d+)/)?.[1] || '未知';
                            const imdbId = doc.querySelector('a[href*="imdb.com/title/"]')?.href?.match(/tt\d+/)?.[0] || '暂无';
                            const runtime = isTv
                                ? doc.querySelector('span[property="v:episodeCount"]')
                                    ? `共${doc.querySelector('span[property="v:episodeCount"]').textContent}集`
                                    : '未知集数'
                                : doc.querySelector('span[property="v:runtime"]')?.textContent.trim() || '未知片长';
                            const intro = doc.querySelector('span[property="v:summary"]')?.textContent.trim().replace(/\s+/g, ' ') ||
                                (isTv ? '暂无电视剧简介' : '暂无电影简介');
                            const posterUrls = await getDoubanOfficialPosters(url);
                            const stillUrls = await getDoubanStillsList(url);
                            resolve({
                                mediaType: isTv ? 'tv' : 'movie',
                                source: '豆瓣',
                                title, genreTags, year, alsoKnown, director, writer, actor,
                                region, release, lang, rating, doubanId, imdbId, runtime, intro,
                                posterUrls,
                                stillUrls,
                                url // 保存原始URL用于后续加载更多
                            });
                        } catch (e) {
                            reject(new Error(`豆瓣解析失败:${e.message}`));
                        }
                    },
                    onerror: () => reject(new Error('豆瓣请求失败')),
                    ontimeout: () => reject(new Error('豆瓣请求超时'))
                });
            } else if (url.includes('themoviedb.org')) {
                const isMovie = url.includes('/movie/');
                const isTv = url.includes('/tv/');
                let mediaType = isMovie ? 'movie' : (isTv ? 'tv' : 'movie');
                const idMatch = url.match(/\/(movie|tv)\/(\d+)/);
                if (!idMatch) {
                    reject(new Error('TMDB链接格式错误(需包含/movie/或/tv/及数字ID)'));
                    return;
                }
                const [, type, id] = idMatch;
                mediaType = type;
                const tmdbDetailUrl = `${TMDB_CONFIG.BASE_URL}/${mediaType}/${id}?api_key=${TMDB_CONFIG.API_KEY}&language=zh-CN&append_to_response=credits`;
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: tmdbDetailUrl,
                    headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                    onload: async (res) => {
                        try {
                            const data = JSON.parse(res.responseText);
                            const title = mediaType === 'movie' ? data.title : data.name;
                            const genreTags = data.genres.map(g => g.name);
                            const year = mediaType === 'movie'
                                ? data.release_date?.split('-')[0]
                                : data.first_air_date?.split('-')[0];
                            const alsoKnown = data.also_known_as?.join(' / ') || '';
                            const director = mediaType === 'movie'
                                ? (data.credits?.crew?.filter(c => c.job === 'Director').map(d => d.name).join(' / ') || '未知')
                                : '未知';
                            const writer = '未知';
                            const actor = (data.credits?.cast || []).slice(0, 6).map(a => a.name).join(',') || '未知';
                            const region = (data.production_countries || []).map(c => c.name).join('、') || '未知';
                            const release = mediaType === 'movie' ? data.release_date : data.first_air_date;
                            const lang = (data.spoken_languages || []).map(l => l.name).join('、') || '未知';
                            const rating = data.vote_average || '暂无';
                            const tmdbId = id;
                            const imdbId = data.imdb_id || '暂无';
                            const runtime = mediaType === 'movie'
                                ? `${data.runtime}分钟`
                                : `${data.number_of_episodes || '未知'}集(共${data.number_of_seasons || '未知'}季)`;
                            const intro = data.overview || (mediaType === 'tv' ? '暂无电视剧简介' : '暂无电影简介');
                            let posterUrls = [];
                            if (data.poster_path) {
                                posterUrls.push(`${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.POSTER_SIZE}/${data.poster_path}`);
                                const postersUrl = `${TMDB_CONFIG.BASE_URL}/${mediaType}/${id}/images?api_key=${TMDB_CONFIG.API_KEY}&include_image_language=zh,en&image_type=poster&sort_by=primary`;
                                await new Promise(resolvePosters => {
                                    GM_xmlhttpRequest({
                                        method: 'GET',
                                        url: postersUrl,
                                        headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                                        onload: (res) => {
                                            try {
                                                const posterData = JSON.parse(res.responseText);
                                                const additionalPosters = safeGet(posterData, 'posters', [])
                                                    .slice(1, TMDB_CONFIG.IMAGE_CANDIDATES_COUNT)
                                                    .map(img => `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.POSTER_SIZE}/${img.file_path}`)
                                                    .filter(Boolean);
                                                posterUrls = [...posterUrls, ...additionalPosters].slice(0, TMDB_CONFIG.IMAGE_CANDIDATES_COUNT);
                                                // 去重处理
                                                posterUrls = removeDuplicateUrls(posterUrls);
                                            } catch (e) {
                                                console.log('获取更多海报失败:', e);
                                            }
                                            resolvePosters();
                                        },
                                        onerror: () => resolvePosters(),
                                        ontimeout: () => resolvePosters()
                                    });
                                });
                            }
                            const stillUrls = await getTMDBStillsList(mediaType, id);
                            resolve({
                                mediaType,
                                source: 'TMDB',
                                title, genreTags, year, alsoKnown, director, writer, actor,
                                region, release, lang, rating, tmdbId, imdbId, runtime, intro,
                                posterUrls,
                                stillUrls,
                                url // 保存原始URL用于后续加载更多
                            });
                        } catch (e) {
                            reject(new Error(`TMDB解析失败:${e.message}`));
                        }
                    },
                    onerror: () => reject(new Error('TMDB请求失败')),
                    ontimeout: () => reject(new Error('TMDB请求超时'))
                });
            } else {
                reject(new Error('不支持的链接类型(仅支持豆瓣、TMDB)'));
            }
        });
    }

    function getHotComments(url) {
        return new Promise(resolve => {
            if (url.includes('themoviedb.org')) {
                resolve([]);
                return;
            }
            const commentUrl = url.replace(/\/subject\/(\d+)\/?$/, '/subject/$1/comments?status=P');
            GM_xmlhttpRequest({
                method: 'GET',
                url: commentUrl,
                headers: { ...COMMON_HEADERS, 'Referer': url, 'Host': new URL(url).hostname },
                timeout: 8000,
                onload: (res) => {
                    try {
                        const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
                        const comments = Array.from(doc.querySelectorAll('.comment-item .short'))
                            .slice(0, 3)
                            .map(elem => elem.textContent.trim())
                            .filter(Boolean);
                        resolve(comments.length ? comments : []);
                    } catch (e) {
                        resolve([]);
                    }
                },
                onerror: () => resolve([]),
                ontimeout: () => resolve([])
            });
        });
    }

    // 加载更多海报功能(每组5张,滚轮触发,去重优化)
    async function loadMorePosters() {
        if (isLoadingPosters) {
            showStatus('正在加载海报,请稍候...', false);
            return;
        }

        if (!currentMovieInfo) {
            showStatus('未找到影视信息,请重新加载', true);
            return;
        }

        isLoadingPosters = true;
        const loadMorePostersBtn = document.getElementById('load-more-posters');
        if (loadMorePostersBtn) {
            loadMorePostersBtn.textContent = '加载中...';
            loadMorePostersBtn.disabled = true;
        }

        try {
            // 首次加载所有海报(带唯一标识)并去重
            if (!window.allPostersWithIds) {
                window.allPostersWithIds = [];
                if (currentMovieInfo.source === '豆瓣') {
                    const rawPosters = await getDoubanOfficialPosters(currentMovieInfo.url);
                    window.allPostersWithIds = rawPosters.map(url => ({
                        url,
                        id: url // 用URL作为唯一标识
                    }));
                } else if (currentMovieInfo.source === 'TMDB' && currentMovieInfo.tmdbId) {
                    const postersUrl = `${TMDB_CONFIG.BASE_URL}/${currentMovieInfo.mediaType}/${currentMovieInfo.tmdbId}/images?api_key=${TMDB_CONFIG.API_KEY}&include_image_language=zh,en&image_type=poster&sort_by=primary`;
                    await new Promise(resolvePosters => {
                        GM_xmlhttpRequest({
                            method: 'GET',
                            url: postersUrl,
                            headers: { 'Authorization': `Bearer ${TMDB_CONFIG.ACCESS_TOKEN}`, 'Content-Type': 'application/json' },
                            onload: (res) => {
                                try {
                                    const posterData = JSON.parse(res.responseText);
                                    window.allPostersWithIds = safeGet(posterData, 'posters', []).map(img => ({
                                        url: `${TMDB_CONFIG.IMAGE_BASE_URL}${TMDB_CONFIG.POSTER_SIZE}/${img.file_path}`,
                                        id: img.file_path // 用TMDB的file_path作为唯一标识
                                    })).filter(img => img.url);
                                    // 去重处理(通过id)
                                    const uniquePosters = [];
                                    const idSet = new Set();
                                    window.allPostersWithIds.forEach(img => {
                                        if (!idSet.has(img.id)) {
                                            idSet.add(img.id);
                                            uniquePosters.push(img);
                                        }
                                    });
                                    window.allPostersWithIds = uniquePosters;
                                } catch (e) {
                                    console.log('获取TMDB海报列表失败:', e);
                                    window.allPostersWithIds = [];
                                }
                                resolvePosters();
                            },
                            onerror: () => {
                                window.allPostersWithIds = [];
                                resolvePosters();
                            },
                            ontimeout: () => {
                                window.allPostersWithIds = [];
                                resolvePosters();
                            }
                        });
                    });
                } else {
                    window.allPostersWithIds = [];
                }
            }

            const allPosters = window.allPostersWithIds || [];
            // 每组5张,计算当前组的起始和结束索引
            const startIndex = posterPage * TMDB_CONFIG.IMAGE_CANDIDATES_COUNT;
            const endIndex = (posterPage + 1) * TMDB_CONFIG.IMAGE_CANDIDATES_COUNT;
            const postersToLoad = allPosters.slice(startIndex, endIndex);

            if (postersToLoad.length > 0) {
                if (posterContainer) {
                    posterContainer.style.display = 'grid';
                    posterContainer.style.gridTemplateColumns = 'repeat(5, 1fr)'; // 每行5张,排版整齐
                    posterContainer.style.gap = '8px'; // 图片间距,避免遮挡

                    for (let i = 0; i < postersToLoad.length; i++) {
                        const poster = postersToLoad[i];
                        if (loadedPosterIds.has(poster.id)) {
                            continue; // 跳过已加载的海报(去重)
                        }
                        loadedPosterIds.add(poster.id); // 标记为已加载

                        try {
                            const dataUrl = await getImageDataURLWithQuality(poster.url);
                            const posterImg = document.createElement('div');
                            posterImg.style.cssText = `
                                width: 150px;
                                height: 190px;
                                flex-shrink: 0;
                                border: 1px solid #e5e7eb;
                                border-radius: 4px;
                                cursor: pointer;
                                overflow: hidden;
                                background: #f9fafb;
                                display: flex;
                                align-items: center;
                                justify-content: center;
                            `;
                            posterImg.innerHTML = `<img src="${dataUrl}" style="width:100%; height:100%; object-fit: contain;" alt="海报 ${startIndex + i + 1}">`;

                            posterImg.addEventListener('click', function () {
                                selectedPosterUrl = dataUrl;
                                document.querySelectorAll('#poster-candidates > div').forEach(el => el.style.border = '1px solid #e5e7eb');
                                this.style.border = '3px solid #3b82f6';
                            });

                            posterContainer.appendChild(posterImg);
                        } catch (e) {
                            console.log(`加载海报 ${startIndex + i + 1} 失败:`, e);
                        }
                    }

                    // 滚动到容器底部,显示新增海报
                    posterContainer.scrollTop = posterContainer.scrollHeight;

                    // 页码递增,准备下一组加载
                    posterPage++;
                    showStatus(`已加载第${posterPage}组海报`, false);
                }
            } else {
                showStatus('已加载全部海报', false);
            }
        } catch (e) {
            console.error('加载更多海报出错:', e);
            showStatus('加载更多海报失败,请稍后重试', true);
        } finally {
            if (loadMorePostersBtn) {
                loadMorePostersBtn.textContent = '加载更多海报';
                loadMorePostersBtn.disabled = false;
            }
            isLoadingPosters = false;
        }
    }

    // 加载更多剧照功能(每组5张,滚轮触发,去重优化)
    async function loadMoreStills() {
        if (isLoadingStills) {
            showStatus('正在加载剧照,请稍候...', false);
            return;
        }

        if (!currentMovieInfo) {
            showStatus('未找到影视信息,请重新加载', true);
            return;
        }

        isLoadingStills = true;
        const loadMoreStillsBtn = document.getElementById('load-more-stills');
        if (loadMoreStillsBtn) {
            loadMoreStillsBtn.textContent = '加载中...';
            loadMoreStillsBtn.disabled = true;
        }

        try {
            // 首次加载所有剧照(带唯一标识)并去重
            if (!window.allStillsWithIds) {
                window.allStillsWithIds = [];
                if (currentMovieInfo.source === '豆瓣') {
                    const rawStills = await getDoubanStillsList(currentMovieInfo.url);
                    window.allStillsWithIds = rawStills.map(url => ({
                        url,
                        id: url // 用URL作为唯一标识
                    }));
                } else if (currentMovieInfo.source === 'TMDB' && currentMovieInfo.tmdbId) {
                    const allStillsUrls = await getTMDBStillsList(currentMovieInfo.mediaType, currentMovieInfo.tmdbId);
                    window.allStillsWithIds = allStillsUrls.map(url => ({
                        url,
                        id: url // 用URL作为唯一标识
                    }));
                } else {
                    window.allStillsWithIds = [];
                }
                // 去重处理(通过id)
                const uniqueStills = [];
                const idSet = new Set();
                window.allStillsWithIds.forEach(img => {
                    if (!idSet.has(img.id)) {
                        idSet.add(img.id);
                        uniqueStills.push(img);
                    }
                });
                window.allStillsWithIds = uniqueStills;
            }

            const allStills = window.allStillsWithIds || [];
            // 每组5张,计算当前组的起始和结束索引
            const startIndex = stillPage * TMDB_CONFIG.IMAGE_CANDIDATES_COUNT;
            const endIndex = (stillPage + 1) * TMDB_CONFIG.IMAGE_CANDIDATES_COUNT;
            const stillsToLoad = allStills.slice(startIndex, endIndex);

            if (stillsToLoad.length > 0) {
                if (stillContainer) {
                    stillContainer.style.display = 'grid';
                    stillContainer.style.gridTemplateColumns = 'repeat(5, 1fr)'; // 每行5张,排版整齐
                    stillContainer.style.gap = '8px'; // 图片间距,避免遮挡

                    for (let i = 0; i < stillsToLoad.length; i++) {
                        const still = stillsToLoad[i];
                        if (loadedStillIds.has(still.id)) {
                            continue; // 跳过已加载的剧照(去重)
                        }
                        loadedStillIds.add(still.id); // 标记为已加载

                        try {
                            const dataUrl = await getImageDataURLWithQuality(still.url);
                            const stillImg = document.createElement('div');
                            stillImg.style.cssText = `
                                width: 200px;
                                height: 110px;
                                flex-shrink: 0;
                                border: 1px solid #e5e7eb;
                                border-radius: 4px;
                                cursor: pointer;
                                overflow: hidden;
                                background: #f9fafb;
                                display: flex;
                                align-items: center;
                                justify-content: center;
                            `;
                            stillImg.innerHTML = `<img src="${dataUrl}" style="width:100%; height:100%; object-fit: contain;" alt="剧照 ${startIndex + i + 1}">`;

                            stillImg.addEventListener('click', function () {
                                selectedStillUrl = dataUrl;
                                document.querySelectorAll('#still-candidates > div').forEach(el => el.style.border = '1px solid #e5e7eb');
                                this.style.border = '3px solid #3b82f6';
                            });

                            stillContainer.appendChild(stillImg);
                        } catch (e) {
                            console.log(`加载剧照 ${startIndex + i + 1} 失败:`, e);
                        }
                    }

                    // 滚动到容器底部,显示新增剧照
                    stillContainer.scrollTop = stillContainer.scrollHeight;

                    // 页码递增,准备下一组加载
                    stillPage++;
                    showStatus(`已加载第${stillPage}组剧照`, false);
                }
            } else {
                showStatus('已加载全部剧照', false);
                if (loadMoreStillsBtn) {
                    loadMoreStillsBtn.textContent = '没有更多剧照了';
                    loadMoreStillsBtn.disabled = true;
                    loadMoreStillsBtn.style.opacity = '0.6';
                }
            }
        } catch (e) {
            console.error('加载更多剧照出错:', e);
            showStatus('加载更多剧照失败,请稍后重试', true);
            if (loadMoreStillsBtn) {
                loadMoreStillsBtn.textContent = '加载失败,重试';
                loadMoreStillsBtn.disabled = false;
            }
        } finally {
            if (loadMoreStillsBtn) {
                loadMoreStillsBtn.textContent = '加载更多剧照';
                loadMoreStillsBtn.disabled = false;
            }
            isLoadingStills = false;
        }
    }

    async function showImageSelection(movieInfo) {
        return new Promise(async (resolve) => {
            if (!posterContainer || !stillContainer) {
                posterContainer = document.getElementById('poster-candidates');
                stillContainer = document.getElementById('still-candidates');
            }
            const imageSelection = document.getElementById('image-selection');
            const loadMorePostersBtn = document.getElementById('load-more-posters');
            const loadMoreStillsBtn = document.getElementById('load-more-stills');

            loadedPosterIds.clear(); // 清空已加载海报标记,重新开始
            posterPage = 0; // 重置海报分页
            loadedStillIds.clear(); // 清空已加载剧照标记,重新开始
            stillPage = 0; // 重置剧照分页

            if (!posterContainer || !stillContainer || !imageSelection) {
                resolve();
                return;
            }
            posterContainer.style.display = 'grid';
            posterContainer.innerHTML = '<div style="color:#6b7280;">加载海报中...</div>';
            stillContainer.innerHTML = '<div style="color:#6b7280;">加载剧照中...</div>';
            imageSelection.style.display = 'block';
            loadMorePostersBtn.style.display = 'none';
            loadMoreStillsBtn.style.display = 'none';

            if (movieInfo.posterUrls && movieInfo.posterUrls.length > 0) {
                posterContainer.style.display = 'grid';
                posterContainer.innerHTML = '';
                selectedPosterUrl = await getImageDataURLWithQuality(movieInfo.posterUrls[0]);

                // 初始加载第一组(5张)海报,标记已加载ID
                const initialPosters = movieInfo.posterUrls.slice(0, TMDB_CONFIG.IMAGE_CANDIDATES_COUNT).map(url => ({
                    url,
                    id: url
                }));
                for (let i = 0; i < initialPosters.length; i++) {
                    const poster = initialPosters[i];
                    loadedPosterIds.add(poster.id); // 标记初始海报为已加载
                    try {
                        const dataUrl = await getImageDataURLWithQuality(poster.url);
                        const posterImg = document.createElement('div');
                        posterImg.style.cssText = `
                            width: 150px; height: 190px; flex-shrink: 0;
                            border: ${i === 0 ? '3px solid #3b82f6' : '1px solid #e5e7eb'};
                            border-radius: 4px; cursor: pointer; overflow: hidden;
                            background: #f9fafb; display: flex; align-items: center; justify-content: center;
                        `;
                        posterImg.innerHTML = `<img src="${dataUrl}" onError="this.src='https://picsum.photos/150/190?default-poster'" style="width:100%; height:100%; object-fit: contain;" alt="海报 ${i + 1}">`;

                        posterImg.addEventListener('click', function () {
                            selectedPosterUrl = dataUrl;
                            document.querySelectorAll('#poster-candidates > div').forEach(el => el.style.border = '1px solid #e5e7eb');
                            this.style.border = '3px solid #3b82f6';
                        });

                        posterContainer.appendChild(posterImg);
                    } catch (e) {
                        console.log(`加载初始海报 ${i + 1} 失败:`, e);
                    }
                }

                // 如果有更多海报,显示“加载更多”按钮
                if (movieInfo.posterUrls.length > TMDB_CONFIG.IMAGE_CANDIDATES_COUNT) {
                    loadMorePostersBtn.style.display = 'inline-block';
                    loadMorePostersBtn.disabled = false;
                }
            } else {
                posterContainer.innerHTML = '<div style="color:#6b7280;">未找到海报</div>';
                selectedPosterUrl = 'https://picsum.photos/800/450?default-poster';
            }

            // 剧照初始加载逻辑:只准备加载更多按钮,不自动加载
            const hasStills = (movieInfo.stillUrls && movieInfo.stillUrls.length > 0) ||
                         (currentMovieInfo.source === '豆瓣' && currentMovieInfo.url) ||
                         (currentMovieInfo.source === 'TMDB' && currentMovieInfo.tmdbId);
            if (hasStills) {
                stillContainer.innerHTML = '<div style="color:#6b7280;">点击“加载更多剧照”查看剧照</div>';
                loadMoreStillsBtn.style.display = 'inline-block';
                loadMoreStillsBtn.disabled = false;
            } else {
                stillContainer.style.display = 'grid';
                stillContainer.innerHTML = '<div style="color:#6b7280;">未找到剧照</div>';
                selectedStillUrl = 'https://picsum.photos/800/450?default-still';
                loadMoreStillsBtn.style.display = 'none';
            }

            resolve();
        });
    }

    // 初始化美化工具
    function initFormatTools() {
        const buttonContainer = document.getElementById('format-buttons');
        const categoryContainer = document.getElementById('format-categories');
        const previewContainer = document.getElementById('format-preview');
        const previewToggle = document.getElementById('format-preview-toggle');

        if (!buttonContainer || !categoryContainer || !previewContainer || !previewToggle) return;

        // 清空容器
        buttonContainer.innerHTML = '';
        categoryContainer.innerHTML = '';

        // 获取所有唯一分类
        const categories = [...new Set(FORMAT_STYLES.map(style => style.category))];

        // 创建分类标签
        categories.forEach(category => {
            const catBtn = document.createElement('div');
            catBtn.textContent = category;
            catBtn.style.cssText = `
                padding:3px 8px; background:#e2e8f0; color:#4b5563; border-radius:4px;
                font-size:12px; cursor:pointer; white-space:nowrap;
            `;

            // 默认选中第一个分类
            if (category === categories[0]) {
                catBtn.style.background = '#3b82f6';
                catBtn.style.color = 'white';
            }

            // 点击分类标签过滤样式
            catBtn.addEventListener('click', () => {
                // 更新分类按钮样式
                document.querySelectorAll('#format-categories > div').forEach(btn => {
                    btn.style.background = '#e2e8f0';
                    btn.style.color = '#4b5563';
                });
                catBtn.style.background = '#3b82f6';
                catBtn.style.color = 'white';

                // 显示选中分类的样式按钮
                document.querySelectorAll('#format-buttons > button').forEach(btn => {
                    const btnCategory = btn.getAttribute('data-category');
                    btn.style.display = btnCategory === category ? 'inline-flex' : 'none';
                });

                // 清空预览
                previewContainer.innerHTML = '<div style="text-align:center; color:#6b7280; font-size:13px;">选择样式查看预览效果</div>';
            });

            categoryContainer.appendChild(catBtn);
        });

        // 创建样式按钮
        FORMAT_STYLES.forEach((style, index) => {
            const btn = document.createElement('button');
            const iconHtml = style.icon ? `<i class="fa ${style.icon}" style="margin-right:4px;"></i>` : '';
            btn.innerHTML = `${iconHtml}${style.name}`;
            btn.setAttribute('data-category', style.category);
            btn.style.cssText = `
                background: #22c55e; color: white; border: none;
                padding: 5px 10px; border-radius: 3px; cursor: pointer;
                font-size: 12px; margin: 1px; display: ${index === 0 ? 'inline-flex' : 'none'};
                align-items: center; display: ${style.category === categories[0] ? 'inline-flex' : 'none'};
            `;

            // 样式预览功能
            if (style.preview) {
                btn.addEventListener('mouseenter', () => {
                    if (previewContainer.style.display === 'block') {
                        previewContainer.innerHTML = `
                            <div style="margin-bottom:5px; font-size:13px; color:#4b5563; font-weight:500;">
                                ${style.name} 预览:
                            </div>
                            <div class="style-preview-content">
                                ${style.apply()}
                            </div>
                        `;
                    }
                });
            }

            // 样式应用功能
            btn.addEventListener('click', async (e) => {
                e.stopPropagation();
                e.preventDefault();

                // 添加点击动画反馈
                btn.style.background = '#166534';
                setTimeout(() => {
                    btn.style.background = '#22c55e';
                }, 200);

                await autoClickSourceBtn();

                const editor = getCurrentEditor();
                if (!editor) {
                    showStatus('未找到编辑框,请先切换到源代码模式', true);
                    return;
                }

                let selectedText = '';
                if (editor.type === 'codemirror') {
                    selectedText = editor.instance.getSelection();
                } else {
                    selectedText = editor.instance.value.substring(
                        editor.instance.selectionStart,
                        editor.instance.selectionEnd
                    );
                }

                const styledHtml = style.apply(selectedText);

                if (editor.type === 'codemirror') {
                    editor.instance.replaceSelection(styledHtml);
                } else {
                    const start = editor.instance.selectionStart;
                    const end = editor.instance.selectionEnd;
                    editor.instance.value = editor.instance.value.substring(0, start) + styledHtml + editor.instance.value.substring(end);
                    editor.instance.dispatchEvent(new Event('input', { bubbles: true }));
                    editor.instance.focus();
                    editor.instance.setSelectionRange(start + styledHtml.length, start + styledHtml.length);
                }

                const saved = await autoClickSaveBtn();
                if (saved) {
                    showStatus(`已应用“${style.name}”并自动保存`, false);
                } else {
                    showStatus(`已应用“${style.name}”,请手动保存`, false);
                }
            });

            buttonContainer.appendChild(btn);
        });

        // 预览区域切换功能
        previewToggle.addEventListener('click', () => {
            if (previewContainer.style.display === 'none') {
                previewContainer.style.display = 'block';
                previewToggle.textContent = '隐藏预览';
            } else {
                previewContainer.style.display = 'none';
                previewToggle.textContent = '显示预览';
            }
        });
    }

    function getCurrentEditor() {
        if (sourceCodeElement && sourceCodeElement.offsetParent !== null) {
            return { type: 'textarea', instance: sourceCodeElement };
        }

        const codeMirror = document.querySelector('.CodeMirror');
        if (codeMirror && codeMirror.CodeMirror) {
            return { type: 'codemirror', instance: codeMirror.CodeMirror };
        }

        const editorSelectors = [
            '#myModal-code textarea',
            'textarea.tox-textarea',
            'textarea.mce-textbox',
            'textarea.cke_source',
            'textarea[name="message"]',
            '#editor_content'
        ];

        for (const selector of editorSelectors) {
            const elem = document.querySelector(selector);
            if (elem && elem.style.display !== 'none') {
                sourceCodeElement = elem;
                return { type: 'textarea', instance: elem };
            }
        }

        return null;
    }

    // 绑定按钮事件
    function bindEventListeners() {
        const fetchBtn = document.getElementById('fetch-btn');
        const mediaUrlInput = document.getElementById('media-url');
        const pasteBtn = document.getElementById('paste-btn');
        const clearBtn = document.getElementById('clear-btn');
        const confirmImagesBtn = document.getElementById('confirm-images-btn');
        const loadMorePostersBtn = document.getElementById('load-more-posters');
        const loadMoreStillsBtn = document.getElementById('load-more-stills');

        if (mediaUrlInput) {
            mediaUrlInput.addEventListener('input', function () {
                if (fetchBtn) {
                    fetchBtn.style.display = this.value.trim() ? 'inline-block' : 'none';
                }
            });
        }

        if (fetchBtn) {
            fetchBtn.addEventListener('click', async function () {
                const url = mediaUrlInput.value.trim();
                if (!url) {
                    showStatus('请输入影视链接', true);
                    return;
                }
                showStatus('正在提取影视信息...', false);
                try {
                    currentMovieInfo = await getBasicInfo(url);
                    currentComments = await getHotComments(url);
                    showStatus('信息提取完成,请选择海报和剧照', false);
                    await showImageSelection(currentMovieInfo);
                } catch (err) {
                    showStatus(`提取失败:${err.message || '未知错误'}`, true);
                }
            });
        }

        if (pasteBtn) {
            pasteBtn.addEventListener('click', async function () {
                const backupHtml = document.getElementById('backup-html').value;
                if (backupHtml) {
                    await autoClickSourceBtn();
                    const filled = await autoFillSourceBox(backupHtml);
                    if (filled) {
                        showStatus('内容已粘贴到编辑框', false);
                    } else {
                        showStatus('内容粘贴失败,请手动粘贴剪贴板内容', true);
                    }
                }
            });
        }

        if (clearBtn) {
            clearBtn.addEventListener('click', function () {
                if (mediaUrlInput) mediaUrlInput.value = '';
                if (document.getElementById('search-movie')) document.getElementById('search-movie').value = '';
                if (document.getElementById('search-results')) document.getElementById('search-results').style.display = 'none';
                if (document.getElementById('image-selection')) document.getElementById('image-selection').style.display = 'none';
                if (posterContainer) posterContainer.innerHTML = '';
                if (stillContainer) stillContainer.innerHTML = '';
                if (fetchBtn) fetchBtn.style.display = 'none';
                selectedPosterUrl = '';
                selectedStillUrl = '';
                currentMovieInfo = null;
                currentComments = [];
                posterPage = 0;
                loadedPosterIds.clear(); // 清空去重标记
                stillPage = 0;
                loadedStillIds.clear(); // 清空剧照去重标记
                showStatus('已清除所有内容', false);
            });
        }

        if (confirmImagesBtn) {
            confirmImagesBtn.addEventListener('click', async function () {
                if (!currentMovieInfo) {
                    showStatus('未找到影视信息,请重新加载', true);
                    return;
                }

                const finalPosterUrl = selectedPosterUrl || 'https://picsum.photos/800/450?default-poster';
                const finalStillUrl = selectedStillUrl || 'https://picsum.photos/800/450?default-still';

                showStatus('正在生成HTML内容...', false);
                const html = generateHTML(currentMovieInfo, currentComments, finalPosterUrl, finalStillUrl);

                const backupHtml = document.getElementById('backup-html');
                if (backupHtml) backupHtml.value = html;

                const success = await fillAndSaveSource(html);
                if (success) {
                    showStatus('内容已填充并自动保存(非发布)', false);
                }
            });
        }

        // 绑定加载更多海报按钮
        if (loadMorePostersBtn) {
            loadMorePostersBtn.addEventListener('click', loadMorePosters);
        }

        // 绑定加载更多剧照按钮
        if (loadMoreStillsBtn) {
            loadMoreStillsBtn.addEventListener('click', loadMoreStills);
        }

        // 绑定海报容器滚轮事件:点击“加载更多海报”后,滚轮可触发加载下一组
        if (posterContainer) {
            posterContainer.addEventListener('wheel', function (e) {
                // 只有点击过“加载更多海报”(posterPage > 0)才开启滚轮加载
                if (posterPage > 0 && !isLoadingPosters) {
                    // 向下滚动时加载下一组
                    if (e.deltaY > 0) {
                        const loadMoreBtn = document.getElementById('load-more-posters');
                        if (loadMoreBtn) {
                            loadMoreBtn.click();
                        }
                    }
                }
            });
        }

        // 绑定剧照容器滚轮事件:点击“加载更多剧照”后,滚轮可触发加载下一组
        if (stillContainer) {
            stillContainer.addEventListener('wheel', function (e) {
                // 只有点击过“加载更多剧照”(stillPage > 0)才开启滚轮加载
                if (stillPage > 0 && !isLoadingStills) {
                    // 向下滚动时加载下一组
                    if (e.deltaY > 0) {
                        const loadMoreBtn = document.getElementById('load-more-stills');
                        if (loadMoreBtn) {
                            loadMoreBtn.click();
                        }
                    }
                }
            });
        }

        setupSearchInteractions();
    }

    // 初始化页面
    function init() {
        // 加载Font Awesome图标
        const faLink = document.createElement('link');
        faLink.rel = 'stylesheet';
        faLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css';
        document.head.appendChild(faLink);

        // 插入控制面板
        insertPanelInMarkedPosition();

        // 检查是否有默认URL
        const urlParams = new URLSearchParams(window.location.search);
        const mediaUrl = urlParams.get('mediaUrl');
        if (mediaUrl && document.getElementById('media-url')) {
            document.getElementById('media-url').value = mediaUrl;
            const fetchBtn = document.getElementById('fetch-btn');
            if (fetchBtn) fetchBtn.style.display = 'inline-block';
        }
    }

    // 启动脚本
    init();

})();