YouTube 一键跳转 Bilibili 同名视频 v2.3

在 YouTube 网站上自动检测当前视频在 Bilibili 是否有同名视频,显示前5个搜索结果供用户选择 - 修复面板状态管理

// ==UserScript==
// @name         YouTube 一键跳转 Bilibili 同名视频 v2.3
// @namespace    https://github.com/your-username/yt-bili-checker
// @version      2.3.0
// @description  在 YouTube 网站上自动检测当前视频在 Bilibili 是否有同名视频,显示前5个搜索结果供用户选择 - 修复面板状态管理
// @author       Maxxie wei
// @license      MIT
// @match        *://www.youtube.com/*
// @match        *://youtube.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @connect      api.bilibili.com
// @connect      www.bilibili.com
// @connect      search.bilibili.com
// @connect      s.search.bilibili.com
// @icon         https://www.google.com/s2/favicons?domain=www.youtube.com
// ==/UserScript==

(function() {
    'use strict';

    // 配置常量
    const CONFIG = {
        BUTTON_ID: 'yt-bili-finder-button-v2',
        RESULTS_PANEL_ID: 'yt-bili-results-panel-v2',
        NOTIFICATION_ID: 'yt-bili-notification-v2',
        DEFAULT_THRESHOLD: 0.3,
        SEARCH_DELAY: 2000,
        NOTIFICATION_DURATION: 5000,
        CACHE_DURATION: 5 * 60 * 1000, // 5分钟
        MAX_SEARCH_RESULTS: 5
    };

    // 全局变量
    let lastUrl = '';
    let mainLogicInterval;
    let currentVideoTitle = '';
    let searchCache = new Map();
    let currentSearchResult = null; // 存储当前搜索结果

    /**
     * 优化的匹配算法
     */
    class OptimizedMatcher {
        constructor() {
            this.threshold = GM_getValue('similarity_threshold', CONFIG.DEFAULT_THRESHOLD);
        }

        /**
         * 标准化标题文本
         */
        normalizeTitle(str) {
            if (!str) return '';
            return str
                .toLowerCase()
                .replace(/[^\p{L}\p{N}\s]/gu, '')
                .replace(/\s+/g, ' ')
                .trim();
        }

        /**
         * 计算两个字符串的相似度 - 使用改进的算法
         */
        calculateStringSimilarity(str1, str2) {
            const s1 = this.normalizeTitle(str1);
            const s2 = this.normalizeTitle(str2);

            if (s1 === s2) return 1.0;
            if (s1.length < 2 || s2.length < 2) return 0.0;

            // 计算共同字符数和位置匹配
            const chars1 = new Set(s1.split(''));
            const chars2 = new Set(s2.split(''));
            
            let commonChars = 0;
            for (const char of chars1) {
                if (chars2.has(char)) {
                    commonChars++;
                }
            }

            // 基础相似度
            const baseSimilarity = (2.0 * commonChars) / (chars1.size + chars2.size);
            
            // 长度相似度
            const lengthSimilarity = Math.min(s1.length, s2.length) / Math.max(s1.length, s2.length);
            
            // 综合相似度
            return (baseSimilarity * 0.7 + lengthSimilarity * 0.3);
        }

        /**
         * 更新匹配阈值
         */
        updateThreshold(newThreshold) {
            if (newThreshold < 0.0 || newThreshold > 1.0) {
                throw new Error('阈值必须在0.0到1.0之间');
            }
            
            this.threshold = newThreshold;
            GM_setValue('similarity_threshold', newThreshold);
            console.log(`匹配阈值已更新为: ${(newThreshold * 100).toFixed(1)}%`);
        }
    }

    /**
     * 简化的标题处理工具
     */
    class SimpleTitleProcessor {
        /**
         * 清理标题中的无用信息
         */
        cleanTitle(title) {
            if (!title) return '';
            
            return title
                .replace(/\[.*?\]/g, '') // 移除方括号内容
                .replace(/【.*?】/g, '') // 移除中文方括号内容
                .replace(/\(.*?\)/g, '') // 移除圆括号内容
                .replace(/(.*?)/g, '') // 移除中文圆括号内容
                .replace(/[【】\[\]()()]/g, '') // 移除所有括号
                .replace(/\s+/g, ' ') // 合并多个空格
                .trim();
        }

        /**
         * 生成搜索关键词 - 直接使用清理后的标题
         */
        generateSearchKeywords(title) {
            const cleanedTitle = this.cleanTitle(title);
            return {
                fullTitle: cleanedTitle,
                originalTitle: title
            };
        }
    }

    // 初始化工具类
    const matcher = new OptimizedMatcher();
    const titleProcessor = new SimpleTitleProcessor();

    /**
     * 设置菜单命令
     */
    function setupMenuCommands() {
        GM_registerMenuCommand('设置匹配阈值', () => {
            const currentThreshold = GM_getValue('similarity_threshold', CONFIG.DEFAULT_THRESHOLD);
            const userInput = prompt('请输入新的匹配阈值 (范围 0.0 - 1.0):', currentThreshold);
            
            if (userInput === null) return;
            
            const newThreshold = parseFloat(userInput);
            if (isNaN(newThreshold) || newThreshold < 0.0 || newThreshold > 1.0) {
                alert('输入无效!请输入一个介于 0.0 和 1.0 之间的数字。');
                return;
            }
            
            try {
                matcher.updateThreshold(newThreshold);
                alert(`匹配阈值已成功保存为: ${(newThreshold * 100).toFixed(1)}%`);
            } catch (error) {
                alert('设置失败: ' + error.message);
            }
        });

        GM_registerMenuCommand('清除搜索缓存', () => {
            searchCache.clear();
            alert('搜索缓存已清除!');
        });

        GM_registerMenuCommand('查看当前设置', () => {
            const threshold = GM_getValue('similarity_threshold', CONFIG.DEFAULT_THRESHOLD);
            alert(`当前匹配阈值: ${(threshold * 100).toFixed(1)}%\n缓存大小: ${searchCache.size}\n搜索结果数: ${CONFIG.MAX_SEARCH_RESULTS}`);
        });

        // 新增:手动搜索当前视频
        GM_registerMenuCommand('手动搜索当前视频', () => {
            const videoTitle = getVideoTitle();
            if (videoTitle) {
                console.log("YT-Bili v2: 手动搜索视频:", videoTitle);
                updateButtonToSearching();
                searchBilibili(videoTitle);
            } else {
                alert('未能获取当前视频标题');
            }
        });

        // 新增:直接跳转到Bilibili搜索页面
        GM_registerMenuCommand('直接跳转Bilibili搜索', () => {
            const videoTitle = getVideoTitle();
            if (videoTitle) {
                const searchKeywords = titleProcessor.generateSearchKeywords(videoTitle);
                const searchUrl = `https://search.bilibili.com/video?keyword=${encodeURIComponent(searchKeywords.fullTitle)}`;
                window.open(searchUrl, '_blank');
            } else {
                alert('未能获取当前视频标题');
            }
        });

        // 新增:测试API连接
        GM_registerMenuCommand('测试Bilibili API连接', () => {
            console.log("YT-Bili v2: 开始测试API连接...");
            testBilibiliAPI();
        });
    }

    /**
     * 主逻辑 - URL侦测器
     */
    function startUrlDetection() {
        let lastVideoId = '';
        
        setInterval(() => {
            const currentUrl = window.location.href;
            const currentVideoId = getVideoId();
            
            // 检查URL变化或视频ID变化
            if (currentUrl !== lastUrl || currentVideoId !== lastVideoId) {
                console.log("YT-Bili v2: 检测到变化,准备刷新按钮...");
                console.log("URL变化:", currentUrl !== lastUrl);
                console.log("视频ID变化:", currentVideoId !== lastVideoId);
                lastUrl = currentUrl;
                lastVideoId = currentVideoId;
                runMainLogic();
            }
        }, 1000);
    }

    /**
     * 获取当前视频ID
     */
    function getVideoId() {
        const urlParams = new URLSearchParams(window.location.search);
        return urlParams.get('v') || '';
    }

    /**
     * 运行主逻辑
     */
    function runMainLogic() {
        clearInterval(mainLogicInterval);
        removeExistingElements();
        
        mainLogicInterval = setInterval(() => {
            if (isVideoPage()) {
                const videoTitle = getVideoTitle();
                if (videoTitle && videoTitle !== currentVideoTitle) {
                    console.log("YT-Bili v2: 检测到新视频标题:", videoTitle);
                    clearInterval(mainLogicInterval);
                    createAndSearch();
                } else if (videoTitle && !document.getElementById(CONFIG.BUTTON_ID)) {
                    console.log("YT-Bili v2: 首次加载视频,创建按钮");
                    clearInterval(mainLogicInterval);
                    createAndSearch();
                }
            }
        }, 500);
    }

    /**
     * 检查是否为视频页面
     */
    function isVideoPage() {
        return window.location.pathname === '/watch' && window.location.search.includes('v=');
    }

    /**
     * 移除已存在的元素
     */
    function removeExistingElements() {
        document.getElementById(CONFIG.BUTTON_ID)?.remove();
        document.getElementById(CONFIG.RESULTS_PANEL_ID)?.remove();
        document.getElementById(CONFIG.NOTIFICATION_ID)?.remove();
        
        // 重置搜索结果状态
        currentSearchResult = null;
    }

    /**
     * 创建按钮并准备搜索
     */
    function createAndSearch() {
        if (document.getElementById(CONFIG.BUTTON_ID)) return;
        
        console.log("YT-Bili v2: 找到视频页面,正在注入按钮...");
        
        const videoTitle = getVideoTitle();
        if (!videoTitle) {
            console.log("YT-Bili v2: 未能获取视频标题,等待页面加载...");
            // 如果标题还没加载,等待一下再试
            setTimeout(() => {
                const retryTitle = getVideoTitle();
                if (retryTitle) {
                    console.log("YT-Bili v2: 重试获取视频标题成功:", retryTitle);
                    currentVideoTitle = retryTitle;
                    createSearchButton();
                } else {
                    console.log("YT-Bili v2: 重试后仍未获取到视频标题");
                }
            }, 2000);
            return;
        }

        currentVideoTitle = videoTitle;
        createSearchButton();
        
        // 设置标题变化监听
        setupTitleChangeListener();
    }

    /**
     * 设置标题变化监听器
     */
    function setupTitleChangeListener() {
        // 监听页面内容变化
        const observer = new MutationObserver((mutations) => {
            mutations.forEach((mutation) => {
                if (mutation.type === 'childList') {
                    const videoTitle = getVideoTitle();
                    if (videoTitle && videoTitle !== currentVideoTitle) {
                        console.log("YT-Bili v2: 检测到标题变化:", videoTitle);
                        currentVideoTitle = videoTitle;
                        removeExistingElements();
                        createSearchButton();
                        // 不再自动搜索,等待用户点击
                    }
                }
            });
        });

        // 监听整个文档的变化
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    /**
     * 获取YouTube视频标题
     */
    function getVideoTitle() {
        const selectors = [
            'h1.ytd-video-primary-info-renderer',
            'h1.ytd-watch-metadata',
            'h1.title',
            '.ytd-video-primary-info-renderer h1',
            'h1[class*="title"]',
            'h1.ytd-video-primary-info-renderer yt-formatted-string',
            'h1.ytd-watch-metadata yt-formatted-string'
        ];

        for (const selector of selectors) {
            const element = document.querySelector(selector);
            if (element && element.textContent.trim()) {
                return element.textContent.trim();
            }
        }

        return null;
    }

    /**
     * 创建搜索按钮
     */
    function createSearchButton() {
        const button = document.createElement('div');
        button.id = CONFIG.BUTTON_ID;
        button.innerHTML = `
            <div style="
                position: fixed;
                top: 12px;
                right: 240px;
                background: linear-gradient(135deg, #00a1d6, #fb7299);
                color: white;
                padding: 8px 12px;
                border-radius: 6px;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-size: 12px;
                cursor: pointer;
                opacity: 0.9;
                transition: opacity 0.3s;
                white-space: nowrap;
                z-index: 10000;
            " onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.9'">
                🔍 Bilibili
            </div>
        `;
        
        // 添加点击事件
        button.onclick = (event) => {
            // 阻止事件冒泡,避免影响YouTube播放
            event.preventDefault();
            event.stopPropagation();
            
            if (currentVideoTitle) {
                console.log("YT-Bili v2: 用户点击搜索按钮,开始搜索:", currentVideoTitle);
                updateButtonToSearching();
                searchBilibili(currentVideoTitle);
            } else {
                console.log("YT-Bili v2: 当前视频标题为空,无法搜索");
                alert('未能获取当前视频标题,请刷新页面重试');
            }
        };
        
        // 直接添加到body,使用固定定位
        document.body.appendChild(button);
        console.log("YT-Bili v2: 按钮已添加到页面顶部");
    }

    /**
     * 更新按钮为搜索中状态
     */
    function updateButtonToSearching() {
        const button = document.getElementById(CONFIG.BUTTON_ID);
        if (!button) return;
        
        button.innerHTML = `
            <div style="
                position: fixed;
                top: 12px;
                right: 240px;
                background: linear-gradient(135deg, #FF9800, #FFC107);
                color: white;
                padding: 8px 12px;
                border-radius: 6px;
                box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                font-size: 12px;
                cursor: pointer;
                opacity: 0.9;
                transition: opacity 0.3s;
                white-space: nowrap;
                z-index: 10000;
            " onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.9'">
                ⏳ 搜索中...
            </div>
        `;
    }

    /**
     * 暂停YouTube视频播放
     */
    function pauseYouTubeVideo() {
        try {
            // 尝试多种方式暂停YouTube视频
            const video = document.querySelector('video');
            if (video && !video.paused) {
                video.pause();
                console.log("YT-Bili v2: 已暂停YouTube视频播放");
            }
            
            // 尝试点击暂停按钮
            const pauseButton = document.querySelector('.ytp-play-button[aria-label*="暂停"], .ytp-play-button[aria-label*="Pause"]');
            if (pauseButton && pauseButton.getAttribute('aria-label').includes('播放')) {
                pauseButton.click();
                console.log("YT-Bili v2: 已点击暂停按钮");
            }
        } catch (error) {
            console.log("YT-Bili v2: 暂停视频时出现错误:", error);
        }
    }

    /**
     * 在Bilibili搜索视频 - 基于官方API文档优化,支持Cookie获取
     */
    function searchBilibili(query) {
        // 检查缓存
        const cacheKey = query.toLowerCase().trim();
        const cachedResult = searchCache.get(cacheKey);
        if (cachedResult && Date.now() - cachedResult.timestamp < CONFIG.CACHE_DURATION) {
            console.log("YT-Bili v2: 使用缓存结果");
            handleSearchResult(cachedResult.data);
            return;
        }

        const searchKeywords = titleProcessor.generateSearchKeywords(query);
        console.log("--- YT-Bili v2 开始查找 ---");
        console.log(`YouTube原始标题: ${query}`);
        console.log(`清理后标题: ${searchKeywords.fullTitle}`);
        console.log(`当前匹配阈值: ${(matcher.threshold * 100).toFixed(1)}%`);
        console.log(`搜索前${CONFIG.MAX_SEARCH_RESULTS}个结果`);

        // 首先获取B站cookies,解决412错误
        getBilibiliCookies().then(() => {
            // 使用新的综合搜索API
            const searchUrl = `https://api.bilibili.com/x/web-interface/wbi/search/all/v2?keyword=${encodeURIComponent(searchKeywords.fullTitle)}&page=1&pagesize=${CONFIG.MAX_SEARCH_RESULTS}`;
            
            GM_xmlhttpRequest({
                method: 'GET',
                url: searchUrl,
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'Referer': 'https://www.bilibili.com/',
                    'Accept': 'application/json, text/plain, */*',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                    'Accept-Encoding': 'gzip, deflate, br',
                    'Connection': 'keep-alive',
                    'Sec-Fetch-Dest': 'empty',
                    'Sec-Fetch-Mode': 'cors',
                    'Sec-Fetch-Site': 'same-site',
                    'Origin': 'https://www.bilibili.com'
                },
                onload: function(response) {
                    console.log("YT-Bili v2: API响应状态:", response.status);
                    console.log("YT-Bili v2: API响应内容:", response.responseText.substring(0, 500));
                    
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const data = JSON.parse(response.responseText);
                            console.log("YT-Bili v2: 解析的API数据:", data);
                            
                            if (data.code === 0 && data.data && data.data.result) {
                                // 新API返回的是综合搜索结果,需要提取视频部分
                                const allResults = data.data.result;
                                let videos = [];
                                
                                // 提取视频结果 - 处理新API的数据结构
                                if (allResults.video && allResults.video.data) {
                                    videos = allResults.video.data.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                } else if (allResults.result && allResults.result.video && allResults.result.video.data) {
                                    // 另一种可能的嵌套结构
                                    videos = allResults.result.video.data.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                } else if (Array.isArray(allResults)) {
                                    // 如果直接是视频数组
                                    videos = allResults.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                } else if (allResults.result && Array.isArray(allResults.result)) {
                                    // 如果result是数组
                                    videos = allResults.result.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                }
                                
                                // 确保视频数据格式正确
                                videos = videos.filter(video => video && video.title && video.bvid);
                                
                                console.log(`YT-Bili v2: 找到 ${videos.length} 个Bilibili视频(前${CONFIG.MAX_SEARCH_RESULTS}个)`);
                                
                                if (videos.length > 0) {
                                    // 计算每个视频的相似度并过滤低匹配度结果
                                    const rankedVideos = videos.map((video, index) => {
                                        const similarity = matcher.calculateStringSimilarity(query, video.title);
                                        console.log(`YT-Bili v2: [${index + 1}/5] "${video.title}" - 相似度: ${(similarity * 100).toFixed(1)}%`);
                                        return {
                                            ...video,
                                            similarity: similarity,
                                            rank: index + 1
                                        };
                                    })
                                    .filter(video => video.similarity >= matcher.threshold) // 过滤低匹配度结果
                                    .sort((a, b) => b.similarity - a.similarity)
                                    .slice(0, CONFIG.MAX_SEARCH_RESULTS); // 限制显示数量
                                    
                                    console.log(`YT-Bili v2: 过滤后剩余 ${rankedVideos.length} 个匹配结果`);
                                    
                                    const result = {
                                        videos: rankedVideos,
                                        totalSearched: videos.length,
                                        threshold: matcher.threshold,
                                        originalTitle: query
                                    };
                                    
                                    // 缓存结果
                                    searchCache.set(cacheKey, {
                                        data: result,
                                        timestamp: Date.now()
                                    });
                                    
                                    handleSearchResult(result);
                                } else {
                                    console.log("YT-Bili v2: 新API返回无视频结果");
                                    // 尝试备用搜索方法
                                    tryAlternativeSearch(query, searchKeywords.fullTitle);
                                }
                            } else {
                                console.log("YT-Bili v2: API返回错误,错误信息:", data);
                                // 尝试备用搜索方法
                                tryAlternativeSearch(query, searchKeywords.fullTitle);
                            }
                        } catch (error) {
                            console.error("YT-Bili v2: 解析响应失败:", error);
                            // 尝试备用搜索方法
                            tryAlternativeSearch(query, searchKeywords.fullTitle);
                        }
                    } else {
                        console.error("YT-Bili v2: API请求失败:", response.status);
                        // 尝试备用搜索方法
                        tryAlternativeSearch(query, searchKeywords.fullTitle);
                    }
                },
                onerror: function(error) {
                    console.error("YT-Bili v2: 网络请求失败:", error);
                    // 尝试备用搜索方法
                    tryAlternativeSearch(query, searchKeywords.fullTitle);
                }
            });
        }).catch(error => {
            console.error("YT-Bili v2: 获取cookies失败:", error);
            // 直接尝试备用搜索方法
            tryAlternativeSearch(query, searchKeywords.fullTitle);
        });
    }

    /**
     * 获取B站cookies - 解决412错误
     */
    function getBilibiliCookies() {
        return new Promise((resolve, reject) => {
            console.log("YT-Bili v2: 开始获取B站cookies...");
            
            GM_xmlhttpRequest({
                method: 'GET',
                url: 'https://www.bilibili.com/',
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
                    'Accept-Encoding': 'gzip, deflate, br',
                    'Connection': 'keep-alive',
                    'Upgrade-Insecure-Requests': '1'
                },
                onload: function(response) {
                    console.log("YT-Bili v2: 成功获取B站cookies,状态码:", response.status);
                    console.log("YT-Bili v2: 响应头:", response.responseHeaders);
                    resolve();
                },
                onerror: function(error) {
                    console.error("YT-Bili v2: 获取cookies失败:", error);
                    reject(error);
                }
            });
        });
    }

    /**
     * 备用搜索方法 - 使用不同的API端点
     */
    function tryAlternativeSearch(originalQuery, searchTitle) {
        console.log("YT-Bili v2: 尝试备用搜索方法...");
        
        // 尝试不同的API端点
        const alternativeUrls = [
            `https://api.bilibili.com/x/web-interface/search/all/v2?keyword=${encodeURIComponent(searchTitle)}&page=1&pagesize=${CONFIG.MAX_SEARCH_RESULTS}`,
            `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(searchTitle)}&order=totalrank&duration=0&tids=0&single_column=0&page=1&pagesize=${CONFIG.MAX_SEARCH_RESULTS}`,
            `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${encodeURIComponent(searchTitle)}&order=update&duration=0&tids=0&single_column=0&page=1&pagesize=${CONFIG.MAX_SEARCH_RESULTS}`
        ];
        
        let currentIndex = 0;
        
        function tryNextUrl() {
            if (currentIndex >= alternativeUrls.length) {
                console.log("YT-Bili v2: 所有备用方法都失败,提供直接跳转");
                handleSearchResult({ 
                    videos: [], 
                    totalSearched: 0, 
                    threshold: matcher.threshold, 
                    originalTitle: originalQuery,
                    fallbackUrl: `https://search.bilibili.com/video?keyword=${encodeURIComponent(searchTitle)}`
                });
                return;
            }
            
            const url = alternativeUrls[currentIndex];
            console.log(`YT-Bili v2: 尝试备用URL ${currentIndex + 1}:`, url);
            
            GM_xmlhttpRequest({
                method: 'GET',
                url: url,
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'Referer': 'https://www.bilibili.com/',
                    'Accept': 'application/json, text/plain, */*',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
                },
                onload: function(response) {
                    console.log(`YT-Bili v2: 备用API ${currentIndex + 1} 响应状态:`, response.status);
                    
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const data = JSON.parse(response.responseText);
                            
                            if (data.code === 0 && data.data && data.data.result) {
                                let videos = [];
                                
                                // 处理不同的API返回格式
                                if (currentIndex === 0) {
                                    // 新API格式
                                    const allResults = data.data.result;
                                    if (allResults.video && allResults.video.data) {
                                        videos = allResults.video.data.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                    } else if (allResults.result && allResults.result.video && allResults.result.video.data) {
                                        videos = allResults.result.video.data.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                    } else if (Array.isArray(allResults)) {
                                        videos = allResults.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                    } else if (allResults.result && Array.isArray(allResults.result)) {
                                        videos = allResults.result.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                    }
                                } else {
                                    // 旧API格式
                                    videos = data.data.result.slice(0, CONFIG.MAX_SEARCH_RESULTS);
                                }
                                
                                // 确保视频数据格式正确
                                videos = videos.filter(video => video && video.title && video.bvid);
                                
                                console.log(`YT-Bili v2: 备用方法 ${currentIndex + 1} 找到 ${videos.length} 个视频`);
                                
                                if (videos.length > 0) {
                                    const rankedVideos = videos.map((video, index) => {
                                        const similarity = matcher.calculateStringSimilarity(originalQuery, video.title);
                                        return {
                                            ...video,
                                            similarity: similarity,
                                            rank: index + 1
                                        };
                                    })
                                    .filter(video => video.similarity >= matcher.threshold) // 过滤低匹配度结果
                                    .sort((a, b) => b.similarity - a.similarity)
                                    .slice(0, CONFIG.MAX_SEARCH_RESULTS); // 限制显示数量
                                    
                                    console.log(`YT-Bili v2: 备用方法 ${currentIndex + 1} 过滤后剩余 ${rankedVideos.length} 个匹配结果`);
                                    
                                    if (rankedVideos.length > 0) {
                                        const result = {
                                            videos: rankedVideos,
                                            totalSearched: videos.length,
                                            threshold: matcher.threshold,
                                            originalTitle: originalQuery
                                        };
                                        
                                        handleSearchResult(result);
                                    } else {
                                        console.log(`YT-Bili v2: 备用方法 ${currentIndex + 1} 过滤后无匹配结果`);
                                        currentIndex++;
                                        tryNextUrl();
                                    }
                                } else {
                                    console.log(`YT-Bili v2: 备用方法 ${currentIndex + 1} 也无视频结果`);
                                    currentIndex++;
                                    tryNextUrl();
                                }
                            } else {
                                console.log(`YT-Bili v2: 备用方法 ${currentIndex + 1} 也无结果`);
                                currentIndex++;
                                tryNextUrl();
                            }
                        } catch (error) {
                            console.error(`YT-Bili v2: 备用方法 ${currentIndex + 1} 解析失败:`, error);
                            currentIndex++;
                            tryNextUrl();
                        }
                    } else {
                        console.error(`YT-Bili v2: 备用API ${currentIndex + 1} 请求失败:`, response.status);
                        currentIndex++;
                        tryNextUrl();
                    }
                },
                onerror: function() {
                    console.error(`YT-Bili v2: 备用方法 ${currentIndex + 1} 网络请求失败`);
                    currentIndex++;
                    tryNextUrl();
                }
            });
        }
        
        tryNextUrl();
    }

    /**
     * 处理搜索结果 - 显示选择面板
     */
    function handleSearchResult(result) {
        // 保存当前搜索结果
        currentSearchResult = result;
        
        updateSearchButton(result);
        
        if (result.videos && result.videos.length > 0) {
            showResultsPanel(result);
        }
    }

    /**
     * 更新搜索按钮状态
     */
    function updateSearchButton(result) {
        const button = document.getElementById(CONFIG.BUTTON_ID);
        if (!button) return;

        if (result.videos && result.videos.length > 0) {
            const bestMatch = result.videos[0];
            const color = bestMatch.similarity >= 0.7 ? '#4CAF50' : bestMatch.similarity >= 0.5 ? '#FFC107' : '#FF9800';
            
            button.innerHTML = `
                <div style="
                    position: fixed;
                    top: 12px;
                    right: 240px;
                    background: ${color};
                    color: white;
                    padding: 8px 12px;
                    border-radius: 6px;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    font-size: 12px;
                    cursor: pointer;
                    opacity: 0.9;
                    transition: opacity 0.3s;
                    white-space: nowrap;
                    z-index: 10000;
                " onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.9'">
                    🎯 ${result.videos.length}个结果
                </div>
            `;
            
            button.onclick = (event) => {
                // 阻止事件冒泡,避免影响YouTube播放
                event.preventDefault();
                event.stopPropagation();
                toggleResultsPanel();
            };
        } else {
            const fallbackUrl = result.fallbackUrl || `https://search.bilibili.com/video?keyword=${encodeURIComponent(titleProcessor.generateSearchKeywords(currentVideoTitle).fullTitle)}`;
            
            button.innerHTML = `
                <div style="
                    position: fixed;
                    top: 12px;
                    right: 240px;
                    background: #FF9800;
                    color: white;
                    padding: 8px 12px;
                    border-radius: 6px;
                    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                    font-size: 12px;
                    cursor: pointer;
                    opacity: 0.9;
                    transition: opacity 0.3s;
                    white-space: nowrap;
                    z-index: 10000;
                " onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0.9'">
                    🔍 Bilibili
                </div>
            `;
            
            button.onclick = (event) => {
                // 阻止事件冒泡
                event.preventDefault();
                event.stopPropagation();
                
                // 暂停YouTube播放(用户要离开页面)
                pauseYouTubeVideo();
                
                // 跳转到Bilibili搜索页面
                window.open(fallbackUrl, '_blank');
            };
        }
    }

    /**
     * 显示搜索结果面板 - 优化UI
     */
    function showResultsPanel(result) {
        // 移除已存在的结果面板
        document.getElementById(CONFIG.RESULTS_PANEL_ID)?.remove();

        const panel = document.createElement('div');
        panel.id = CONFIG.RESULTS_PANEL_ID;
        panel.style.cssText = `
            position: fixed;
            top: 80px;
            right: 20px;
            background: white;
            border-radius: 12px;
            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
            z-index: 10000;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            max-width: 450px;
            max-height: 600px;
            overflow-y: auto;
            animation: slideIn 0.3s ease-out;
            border: 1px solid #e0e0e0;
        `;

        let panelContent = `
            <div style="padding: 20px; border-bottom: 1px solid #eee; background: linear-gradient(135deg, #f8f9fa, #ffffff);">
                <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
                    <h3 style="margin: 0; color: #333; font-size: 18px; font-weight: 600;">🎯 Bilibili搜索结果</h3>
                    <button id="yt-bili-close-panel-v2" style="
                        background: none;
                        border: none;
                        font-size: 20px;
                        cursor: pointer;
                        color: #666;
                        padding: 0;
                        width: 28px;
                        height: 28px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        border-radius: 50%;
                        transition: background-color 0.2s;
                    " onmouseover="this.style.backgroundColor='#f0f0f0'" onmouseout="this.style.backgroundColor='transparent'">×</button>
                </div>
                <div style="font-size: 13px; color: #666; margin-bottom: 8px;">
                    找到 ${result.videos.length} 个结果,按相似度排序
                </div>
                <div style="font-size: 12px; color: #999; font-style: italic;">
                    原始标题: "${result.originalTitle}"
                </div>
            </div>
        `;

        if (result.videos && result.videos.length > 0) {
            result.videos.forEach((video, index) => {
                const similarity = video.similarity;
                const color = similarity >= 0.7 ? '#4CAF50' : similarity >= 0.5 ? '#FFC107' : '#FF9800';
                const rank = video.rank;
                
                // 确保视频信息完整
                const title = video.title || '未知标题';
                const author = video.author || '未知作者';
                const duration = video.duration || '未知时长';
                const bvid = video.bvid || '';
                
                panelContent += `
                    <div class="yt-bili-video-item-v2" data-bvid="${bvid}" style="
                        padding: 16px 20px;
                        border-bottom: 1px solid #f0f0f0;
                        cursor: pointer;
                        transition: all 0.2s;
                        position: relative;
                    " onmouseover="this.style.backgroundColor='#f8f9fa'; this.style.transform='translateX(-2px)'" onmouseout="this.style.backgroundColor='white'; this.style.transform='translateX(0)'">
                        <div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 8px;">
                            <div style="font-weight: 500; color: #333; font-size: 14px; line-height: 1.4; flex: 1; margin-right: 12px;">
                                ${title}
                            </div>
                            <div style="
                                background: ${color};
                                color: white;
                                padding: 4px 8px;
                                border-radius: 6px;
                                font-size: 12px;
                                font-weight: 600;
                                white-space: nowrap;
                                box-shadow: 0 2px 4px rgba(0,0,0,0.1);
                            ">
                                ${(similarity * 100).toFixed(0)}%
                            </div>
                        </div>
                        <div style="font-size: 12px; color: #666; margin-bottom: 6px;">
                            <span style="color: #00a1d6;">👤</span> ${author} | <span style="color: #fb7299;">⏱️</span> ${duration} | <span style="color: #999;">#${rank}</span>
                        </div>
                        <div style="font-size: 11px; color: #999; display: flex; align-items: center;">
                            <span style="margin-right: 4px;">🔗</span> 点击跳转到Bilibili观看
                            <span style="margin-left: auto; font-size: 10px; opacity: 0.7;">→</span>
                        </div>
                    </div>
                `;
            });
        } else {
            // 没有匹配结果时的提示
            panelContent += `
                <div style="padding: 20px; text-align: center; color: #666;">
                    <div style="font-size: 16px; margin-bottom: 8px;">🔍</div>
                    <div style="font-size: 14px; margin-bottom: 4px;">未找到匹配的视频</div>
                    <div style="font-size: 12px; color: #999;">匹配度低于 ${(matcher.threshold * 100).toFixed(1)}% 的结果已被过滤</div>
                </div>
            `;
        }

        panelContent += `
            <div style="padding: 16px 20px; text-align: center; border-top: 1px solid #eee; background: #fafafa;">
                <button id="yt-bili-open-search-v2" style="
                    background: linear-gradient(135deg, #00a1d6, #fb7299);
                    color: white;
                    border: none;
                    padding: 10px 20px;
                    border-radius: 6px;
                    cursor: pointer;
                    font-size: 13px;
                    font-weight: 500;
                    transition: transform 0.2s;
                    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
                " onmouseover="this.style.transform='translateY(-1px)'" onmouseout="this.style.transform='translateY(0)'">在Bilibili中搜索更多结果</button>
            </div>
        `;

        panel.innerHTML = panelContent;

        // 添加动画样式
        const style = document.createElement('style');
        style.textContent = `
            @keyframes slideIn {
                from {
                    transform: translateX(100%);
                    opacity: 0;
                }
                to {
                    transform: translateX(0);
                    opacity: 1;
                }
            }
        `;
        document.head.appendChild(style);

        // 添加到页面
        document.body.appendChild(panel);

        // 绑定事件
        document.getElementById('yt-bili-close-panel-v2').addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();
            panel.remove();
            console.log("YT-Bili v2: 用户关闭结果面板");
        });

        document.getElementById('yt-bili-open-search-v2').addEventListener('click', (event) => {
            event.preventDefault();
            event.stopPropagation();
            
            // 暂停YouTube播放(用户要离开页面)
            pauseYouTubeVideo();
            
            const searchKeywords = titleProcessor.generateSearchKeywords(currentVideoTitle);
            const searchUrl = `https://search.bilibili.com/video?keyword=${encodeURIComponent(searchKeywords.fullTitle)}`;
            window.open(searchUrl, '_blank');
            panel.remove();
            console.log("YT-Bili v2: 用户跳转到Bilibili搜索页面");
        });

        // 绑定视频项点击事件
        const videoItems = panel.querySelectorAll('.yt-bili-video-item-v2');
        videoItems.forEach(item => {
            item.addEventListener('click', (event) => {
                event.preventDefault();
                event.stopPropagation();
                
                // 暂停YouTube播放(用户要跳转到Bilibili视频)
                pauseYouTubeVideo();
                
                const bvid = item.getAttribute('data-bvid');
                if (bvid) {
                    window.open(`https://www.bilibili.com/video/${bvid}`, '_blank');
                }
            });
        });

        // 点击面板外部关闭
        document.addEventListener('click', function closePanel(e) {
            if (!panel.contains(e.target) && e.target.id !== CONFIG.BUTTON_ID) {
                panel.remove();
                document.removeEventListener('click', closePanel);
                console.log("YT-Bili v2: 用户点击面板外部关闭");
            }
        });
    }

    /**
     * 切换结果面板显示/隐藏
     */
    function toggleResultsPanel() {
        const panel = document.getElementById(CONFIG.RESULTS_PANEL_ID);
        if (panel) {
            panel.remove();
            console.log("YT-Bili v2: 隐藏结果面板");
        } else {
            // 重新显示面板
            if (currentSearchResult && currentSearchResult.videos && currentSearchResult.videos.length > 0) {
                console.log("YT-Bili v2: 重新显示结果面板");
                showResultsPanel(currentSearchResult);
            } else {
                console.log("YT-Bili v2: 无搜索结果,重新搜索");
                // 如果没有搜索结果,重新搜索
                if (currentVideoTitle) {
                    updateButtonToSearching();
                    searchBilibili(currentVideoTitle);
                } else {
                    console.log("YT-Bili v2: 当前视频标题为空,无法重新搜索");
                    alert('当前视频标题为空,请刷新页面重试');
                }
            }
        }
    }

    /**
     * 测试Bilibili API连接
     */
    function testBilibiliAPI() {
        console.log("YT-Bili v2: 开始测试API连接...");
        
        // 首先获取cookies
        getBilibiliCookies().then(() => {
            const testUrl = 'https://api.bilibili.com/x/web-interface/wbi/search/all/v2?keyword=测试&page=1&pagesize=5';
            
            GM_xmlhttpRequest({
                method: 'GET',
                url: testUrl,
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
                    'Referer': 'https://www.bilibili.com/',
                    'Accept': 'application/json, text/plain, */*',
                    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
                },
                onload: function(response) {
                    console.log("YT-Bili v2: API测试响应状态:", response.status);
                    console.log("YT-Bili v2: API测试响应内容:", response.responseText.substring(0, 1000));
                    
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const data = JSON.parse(response.responseText);
                            if (data.code === 0) {
                                alert(`API连接成功!\n状态码: ${response.status}\n返回数据: ${JSON.stringify(data, null, 2).substring(0, 500)}`);
                            } else {
                                alert(`API连接成功但返回错误:\n错误码: ${data.code}\n错误信息: ${data.message || '未知错误'}`);
                            }
                        } catch (error) {
                            alert(`API连接成功但解析失败:\n${error.message}\n原始响应:\n${response.responseText.substring(0, 500)}`);
                        }
                    } else {
                        alert(`API连接失败:\n状态码: ${response.status}\n响应内容:\n${response.responseText.substring(0, 500)}`);
                    }
                },
                onerror: function(error) {
                    alert(`API网络请求失败:\n${error.message || '未知网络错误'}`);
                }
            });
        }).catch(error => {
            alert(`获取cookies失败:\n${error.message || '未知错误'}`);
        });
    }

    // 初始化
    setupMenuCommands();
    startUrlDetection();
    
    console.log('YouTube-Bilibili 检测器油猴脚本 v2.3 已加载');
    console.log(`配置: 搜索前${CONFIG.MAX_SEARCH_RESULTS}个结果,默认阈值${(CONFIG.DEFAULT_THRESHOLD * 100).toFixed(1)}%`);
    console.log('基于官方API文档优化,解决412错误');
    console.log('新增Cookie获取机制,确保API正常访问');
    console.log('使用新版综合搜索API: /x/web-interface/wbi/search/all/v2');
    console.log('智能过滤低匹配度结果,避免显示"undefined"');
    console.log('增强的数据解析,支持多种API返回格式');
    console.log('修复面板状态管理,解决返回后按钮无响应问题');
    console.log('增强的视频切换检测已启用');
    console.log('用户点击触发搜索模式已启用');
    console.log('多重备用搜索机制已启用');
    console.log('使用说明:');
    console.log('1. 访问YouTube视频页面');
    console.log('2. 点击右上角的"🔍 Bilibili"按钮');
    console.log('3. 等待搜索结果并选择跳转');
    console.log('4. 或使用菜单中的"直接跳转Bilibili搜索"功能');
    console.log('5. 如遇API问题,可使用"测试Bilibili API连接"菜单命令');
    console.log('6. 低匹配度结果会自动过滤,只显示高质量匹配');
    console.log('7. 从Bilibili返回后,点击结果按钮可重新显示面板');
})();