您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
https://mikanani.me/ 蜜柑计划增加播放器播放按钮,下载,在线播放
// ==UserScript== // @name 蜜柑计划(mikanani.me)调用bitplay增加在线播放按钮 // @namespace https://mikanani.me/ // @version 3.5 // @description https://mikanani.me/ 蜜柑计划增加播放器播放按钮,下载,在线播放 // @author Iko // @license CC BY-NC // @match https://mikanani.me/* // @grant GM.xmlHttpRequest // @grant GM.setValue // @grant GM.getValue // @grant GM.registerMenuCommand // @grant GM.unregisterMenuCommand // @connect bitplay.to // @icon https://mikanani.me/images/favicon.ico?v=2 // ==/UserScript== (function () { 'use strict'; // 服务器配置 const SERVER_LIST_URL = ''; //远程配置服务器url response:{"serverList":[{"name":"bitplay","url":"https://bitplay.to"}]} let SERVER_LIST = [ { name: 'bitplay', url: 'https://bitplay.to', ping: 0 } ]; const DEFAULT_SERVER_INDEX = 0; const STORAGE_KEY = 'bitplay_server_index'; const SERVER_LIST_STORAGE_KEY = 'bitplay_server_list'; const TORRENT_REFRESH_INTERVAL = 10 * 60 * 1000; // 10分钟种子刷新间隔 const SERVER_LIST_CACHE_MAX_AGE = 12 * 60 * 60 * 1000; // 12小时服务器信息缓存时间 let currentServer = SERVER_LIST[DEFAULT_SERVER_INDEX]; let serverInfoDiv = null; let torrentIntervals = {}; // 存储种子定时器 let mutationObserver = null; // DOM变化监听器 let menuCommandIds = []; // 存储菜单命令ID // 检测操作系统类型 function detectOS() { const platform = navigator.platform.toLowerCase(); console.log("platform:", platform); if (platform.includes('win')) return 'Windows'; if (platform.includes('mac')) return 'MacOS'; if (platform.includes('linux')) return 'Linux'; if (platform.includes('ipad')) return 'ipad'; return 'Unknown'; } // 页面类型检测函数 function isHomePage() { return location.pathname === '/' || location.pathname === '/index'; } function isBangumiPage() { return location.pathname.startsWith('/Home/Bangumi/') || location.pathname.startsWith('/Home/Search') || location.pathname.startsWith('/Home/Classic'); } async function fetchServerList() { return new Promise((resolve, reject) => { makeRequest({ method: 'GET', url: SERVER_LIST_URL, onload(res) { try { const data = JSON.parse(res.responseText); if (data && Array.isArray(data.serverList)) { SERVER_LIST = data.serverList.map(s => ({ ...s, ping: 0 })); GM.setValue( SERVER_LIST_STORAGE_KEY, JSON.stringify({ time: Date.now(), list: data.serverList }) ); resolve(); } else { reject(new Error('Invalid format')); } } catch (e) { reject(e); } }, onerror: () => reject(new Error('Request failed')), ontimeout: () => reject(new Error('Request timeout')) }); }); } async function refreshServerList() { const oldUrl = currentServer.url; await fetchServerList(); let idx = 0; for (let i = 0; i < SERVER_LIST.length; i++) { if (SERVER_LIST[i].url == oldUrl) { idx = i; break; } } await GM.setValue(STORAGE_KEY, idx); currentServer = SERVER_LIST[idx]; await testServerListPing(); registerMenuCommands(); } async function loadServerListFromCache() { try { const cached = await GM.getValue(SERVER_LIST_STORAGE_KEY, null); if (cached) { const data = JSON.parse(cached); console.log("存在缓存信息", data) if (data && Array.isArray(data.list)) { // 缓存有效 if (Date.now() - data.time < SERVER_LIST_CACHE_MAX_AGE) { console.log("缓存有效"); SERVER_LIST = data.list.map(s => ({ ...s, ping: 0 })); return; } console.log("缓存无效"); } return; } console.log("不存在缓存信息") } catch (e) { console.error('读取服务器列表缓存失败:', e); } } // 网络请求封装 function makeRequest(options) { console.log('=== 发起网络请求 ==='); console.log('方法:', options.method); console.log('URL:', options.url); const originalOnload = options.onload; const originalOnerror = options.onerror; const originalOntimeout = options.ontimeout; options.onload = function (response) { console.log('=== 收到网络响应 ==='); console.log('内容:', response.responseText) console.log('状态码:', response.status); if (originalOnload) originalOnload(response); }; options.onerror = function (error) { console.log('=== 网络请求错误 ==='); console.log('错误信息:', error); if (originalOnerror) originalOnerror(error); }; options.ontimeout = function () { console.log('=== 网络请求超时 ==='); if (originalOntimeout) originalOntimeout(); }; return GM.xmlHttpRequest(options); } // Ping测试功能 async function measurePing(url) { return new Promise((resolve, reject) => { const startTime = performance.now(); const timeoutId = setTimeout(() => { reject(new Error('Timeout exceeded')); }, 5000); // Request options const options = { method: 'HEAD', url: url + '/assets/favicon.png', onload: (response) => { clearTimeout(timeoutId); const endTime = performance.now(); console.log('Ping成功:', url); resolve(Math.round(endTime - startTime)); }, onerror: (error) => { clearTimeout(timeoutId); console.log(`Ping测试失败: ${url}`, error); resolve(9999999999); }, ontimeout: () => { clearTimeout(timeoutId); console.log('请求超时'); resolve(9999999999); } }; // Call makeRequest to send the request makeRequest(options); }); } async function testCurrentServerPing() { console.log('开始测试当前服务器ping...'); let ping = 9999999999; try{ ping = await measurePing(currentServer.url); }catch(error){ console.error('服务器连接失败', error); } currentServer.ping = ping; console.log(`服务器 ${currentServer.name} ping: ${ping}ms`); updateServerDisplay(); } async function testServerListPing() { console.log('开始测试服务器ping...'); for(let i =0;i<SERVER_LIST.length;i++){ let ping = 9999999999; try{ ping = await measurePing(currentServer.url); }catch(error){ console.error('服务器连接失败', error); } SERVER_LIST[i].ping = ping; if (9999999999 == currentServer.ping) { console.log(`服务器 ${SERVER_LIST[i].name} 无效`); } else { console.log(`服务器 ${SERVER_LIST[i].name} ping: ${ping}ms`); } } updateServerDisplay(); } function updateServerDisplay() { if (serverInfoDiv) { const pingText = currentServer.ping === 9999999999 ? '超时' : `${currentServer.ping}ms`; const pingColor = currentServer.ping < 100 ? '#4CAF50' : currentServer.ping < 500 ? '#FF9800' : '#F44336'; serverInfoDiv.innerHTML = ` 当前播放服务器: ${currentServer.name} <span style="color: ${pingColor}; font-weight: bold;">(${pingText})</span> `; } } // 服务器管理 async function setServerIndex(index) { if (index >= 0 && index < SERVER_LIST.length) { try { await GM.setValue(STORAGE_KEY, index); currentServer = SERVER_LIST[index]; serverInfoDiv.textContent = `当前播放服务器: ${currentServer.name} (测试中...)`; await testCurrentServerPing(); updateServerDisplay(); registerMenuCommands(); } catch (error) { console.error('保存服务器配置失败:', error); alert('切换服务器失败,请稍后重试'); } } } async function registerMenuCommands() { if (typeof GM.unregisterMenuCommand === 'function') { menuCommandIds.forEach(id => { console.log("注销菜单id:", id) try { GM.unregisterMenuCommand(id); } catch (e) { console.error(e); } }); menuCommandIds = []; } SERVER_LIST.forEach((server, index) => { const prefix = (server.url === currentServer.url) ? '✓ ' : ''; GM.registerMenuCommand( 9999999999 == server.ping? `${prefix}切换到服务器: ${server.name}(超时)`:`${prefix}切换到服务器: ${server.name}(${server.ping}ms)`, () => setServerIndex(index), `${index + 1}` ).then(id => { if (typeof id !== 'undefined') { console.log("注册(不可用)菜单id:", id) menuCommandIds.push(id); } }) }); GM.registerMenuCommand( '🔄 刷新服务器列表', async () => { try { await refreshServerList(); } catch (e) { console.error('刷新服务器列表失败:', e); alert('刷新服务器列表失败,请稍后重试'); } }, 'r' ).then(id => { if (typeof id !== 'undefined') { menuCommandIds.push(id); } }); } // 种子管理 function setupTorrentRefresh(magnet, infoHash) { if (torrentIntervals[infoHash]) { clearInterval(torrentIntervals[infoHash]); } torrentIntervals[infoHash] = setInterval(() => { refreshTorrent(magnet, infoHash); }, TORRENT_REFRESH_INTERVAL); console.log(`已为种子 ${infoHash} 设置定时刷新,间隔10分钟`); } function refreshTorrent(magnet, infoHash) { makeRequest({ method: 'POST', url: `${currentServer.url}/api/v1/torrent/add`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ magnet }), onload(res) { if (res.status >= 200 && res.status < 300) { console.log(`种子 ${infoHash} 刷新成功`); } else { console.error(`种子 ${infoHash} 刷新失败,状态码:${res.status}`); } }, onerror(err) { console.error(`种子 ${infoHash} 刷新出错:${err}`); } }); } // 批量下载所有文件 function downloadAllFiles(files, infoHash) { let currentIndex = 0; function downloadNext() { if (currentIndex < files.length) { const file = files[currentIndex]; console.log(`正在下载第 ${currentIndex + 1}/${files.length} 个文件: ${file.name}`); downloadFile(infoHash, file.index, file.name); currentIndex++; // 0.5秒后下载下一个文件 setTimeout(downloadNext, 500); } else { console.log('所有文件下载完成'); } } downloadNext(); } // 文件选择对话框 function createFileSelectionDialog(files, infoHash, actionType) { const overlay = document.createElement('div'); Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', backgroundColor: 'rgba(0, 0, 0, 0.7)', zIndex: '10000', display: 'flex', alignItems: 'center', justifyContent: 'center' }); const dialog = document.createElement('div'); Object.assign(dialog.style, { backgroundColor: '#fff', borderRadius: '8px', padding: '20px', maxWidth: '600px', maxHeight: '70%', overflow: 'auto', boxShadow: '0 4px 20px rgba(0, 0, 0, 0.3)' }); const title = document.createElement('h3'); title.textContent = actionType === 'download' ? '请选择要下载的文件' : '请选择要播放的文件'; title.style.marginTop = '0'; title.style.marginBottom = '20px'; title.style.color = '#333'; dialog.appendChild(title); const fileList = document.createElement('div'); files.forEach((file, index) => { const fileItem = document.createElement('div'); Object.assign(fileItem.style, { padding: '12px', margin: '8px 0', border: '1px solid #ddd', borderRadius: '4px', cursor: 'pointer', transition: 'all 0.2s' }); fileItem.innerHTML = ` <div style="font-weight: bold; margin-bottom: 4px;">${file.name}</div> <div style="color: #666; font-size: 12px;">大小: ${formatSize(file.size)}</div> `; fileItem.addEventListener('mouseenter', () => { fileItem.style.backgroundColor = '#f5f5f5'; fileItem.style.borderColor = '#007cba'; }); fileItem.addEventListener('mouseleave', () => { fileItem.style.backgroundColor = ''; fileItem.style.borderColor = '#ddd'; }); fileItem.addEventListener('click', () => { overlay.remove(); if (actionType === 'download') { downloadFile(infoHash, file.index, file.name); } else if (actionType === 'web_play') { playFileInBrowser(currentServer.url, infoHash, file.index, file.name); } else if (actionType === 'local_play') { playFileWithLocalPlayer(currentServer.url, infoHash, file.index, file.name); } }); fileList.appendChild(fileItem); }); dialog.appendChild(fileList); // 按钮容器 const buttonContainer = document.createElement('div'); buttonContainer.style.marginTop = '20px'; buttonContainer.style.display = 'flex'; buttonContainer.style.justifyContent = 'flex-end'; buttonContainer.style.gap = '10px'; // 如果是下载操作且有多个文件,添加"下载全部"按钮 if (actionType === 'download' && files.length > 1) { const downloadAllButton = document.createElement('button'); downloadAllButton.textContent = '⬇ 下载全部'; Object.assign(downloadAllButton.style, { padding: '8px 16px', backgroundColor: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); downloadAllButton.addEventListener('click', () => { overlay.remove(); downloadAllFiles(files, infoHash); }); buttonContainer.appendChild(downloadAllButton); } const cancelButton = document.createElement('button'); cancelButton.textContent = '取消'; Object.assign(cancelButton.style, { padding: '8px 16px', backgroundColor: '#ccc', color: '#333', border: 'none', borderRadius: '4px', cursor: 'pointer' }); cancelButton.addEventListener('click', () => { overlay.remove(); }); buttonContainer.appendChild(cancelButton); dialog.appendChild(buttonContainer); overlay.appendChild(dialog); document.body.appendChild(overlay); } // 格式化文件大小 function formatSize(bytes) { if (bytes === 0) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // 新的下载文件函数 function downloadFile(infoHash, fileIndex, fileName) { const downloadURL = `${currentServer.url}/api/v1/torrent/${infoHash}/stream/${fileIndex}/${fileName}.mp4`; console.log(`下载文件: ${downloadURL}`); window.open(downloadURL, '_blank'); } // 获取文件列表 function getFileList(infoHash, callback, btn, defaultText, actionType) { makeRequest({ method: 'GET', url: `${currentServer.url}/api/v1/torrent/${infoHash}`, onload(listRes) { if (listRes.status >= 200 && listRes.status < 300) { try { const files = JSON.parse(listRes.responseText); if (files && Array.isArray(files)) { callback(files, infoHash, btn, defaultText, actionType); } else { handleButtonError(btn, '文件列表格式错误', defaultText); } } catch (error) { handleButtonError(btn, '解析文件列表失败:' + error.message, defaultText); } } else { handleButtonError(btn, '获取文件列表失败,状态码:' + listRes.status, defaultText); } }, onerror: () => handleButtonError(btn, '获取文件列表出错', defaultText), ontimeout: () => handleButtonError(btn, '获取文件列表超时', defaultText) }); } // 播放和下载实现函数 function playFileWithLocalPlayer(host, infoHash, fileIndex, fileName) { const streamURL = `${host}/api/v1/torrent/${infoHash}/stream/${fileIndex}/stream.mp4`; const os = detectOS(); console.log(`使用本地播放器播放: ${streamURL}`); if (os === 'MacOS') { window.open(`iina://weblink?url=${streamURL}`); } else if (os === 'Windows') { window.open(`potplayer://${streamURL}`); } else if (os === 'ipad') { window.location.href = `Alook://${streamURL}`; } else { alert('检测到未知系统,播放链接:\n' + streamURL); } } function playFileInBrowser(host, infoHash, fileIndex, fileName) { const streamURL = `${currentServer.url}/api/v1/torrent/${infoHash}/stream/${fileIndex}/stream.mp4`; console.log(`在浏览器中播放: ${streamURL}`); const success = window.open(streamURL, '_blank'); if (!success) { window.location.href = streamURL; } } // 按钮状态管理 function handleButtonError(btn, message, defaultText) { alert(message); resetButton(btn, defaultText); } function resetButton(btn, text) { btn.disabled = false; btn.textContent = text; } // 按钮事件处理函数 function handleLocalPlayButtonClick(btn, magnet, infoHash) { const host = currentServer.url; btn.disabled = true; btn.textContent = '加载中…'; makeRequest({ method: 'POST', url: `${host}/api/v1/torrent/add`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ magnet }), onload(res) { if (res.status >= 200 && res.status < 300) { setupTorrentRefresh(magnet, infoHash); btn.textContent = '获取文件列表中…'; setTimeout(() => { getFileList(infoHash, handleFileListForAction, btn, '▶ 播放器播放', 'local_play'); }, 1000); } else { handleButtonError(btn, '添加失败,状态码:' + res.status, '▶ 播放器播放'); } }, onerror: () => handleButtonError(btn, '请求出错', '▶ 播放器播放'), ontimeout: () => handleButtonError(btn, '请求超时', '▶ 播放器播放') }); } function handleWebPlayButtonClick(btn, magnet, infoHash) { const host = currentServer.url; btn.disabled = true; btn.textContent = '加载中…'; makeRequest({ method: 'POST', url: `${host}/api/v1/torrent/add`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ magnet }), onload(res) { if (res.status >= 200 && res.status < 300) { setupTorrentRefresh(magnet, infoHash); btn.textContent = '获取文件列表中…'; setTimeout(() => { getFileList(infoHash, handleFileListForAction, btn, '🌐 网页播放', 'web_play'); }, 1000); } else { handleButtonError(btn, '添加失败,状态码:' + res.status, '🌐 网页播放'); } }, onerror: () => handleButtonError(btn, '请求出错', '🌐 网页播放'), ontimeout: () => handleButtonError(btn, '请求超时', '🌐 网页播放') }); } function handleDownloadButtonClick(btn, magnet, infoHash) { const host = currentServer.url; btn.disabled = true; btn.textContent = '加载中…'; makeRequest({ method: 'POST', url: `${host}/api/v1/torrent/add`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify({ magnet }), onload(res) { if (res.status >= 200 && res.status < 300) { setupTorrentRefresh(magnet, infoHash); btn.textContent = '获取文件列表中…'; setTimeout(() => { getFileList(infoHash, handleFileListForAction, btn, '⬇ 下载', 'download'); }, 1000); } else { handleButtonError(btn, '添加失败,状态码:' + res.status, '⬇ 下载'); } }, onerror: () => handleButtonError(btn, '请求出错', '⬇ 下载'), ontimeout: () => handleButtonError(btn, '请求超时', '⬇ 下载') }); } // 处理文件列表 function handleFileListForAction(files, infoHash, btn, defaultText, actionType) { if (files.length === 1) { if (actionType === 'download') { downloadFile(infoHash, 0, files[0].name); } else if (actionType === 'web_play') { playFileInBrowser(currentServer.url, infoHash, 0, files[0].name); } else if (actionType === 'local_play') { playFileWithLocalPlayer(currentServer.url, infoHash, 0, files[0].name); } } else if (files.length > 1) { btn.textContent = '请选择文件…'; createFileSelectionDialog(files, infoHash, actionType); } else { handleButtonError(btn, '没有找到可用的文件', defaultText); } resetButton(btn, defaultText); } // 创建播放器播放按钮(简化版) function createLocalPlayButton(magnet, infoHash) { const btn = document.createElement('button'); btn.textContent = '▶ 播放器播放'; Object.assign(btn.style, { marginLeft: '10px', padding: '2px 6px', background: '#2196F3', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); btn.addEventListener('click', () => { handleLocalPlayButtonClick(btn, magnet, infoHash); }); return btn; } // 创建网页播放按钮 function createWebPlayButton(magnet, infoHash) { const btn = document.createElement('button'); btn.textContent = '🌐 网页播放'; Object.assign(btn.style, { marginLeft: '5px', padding: '2px 6px', background: '#FF9800', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); btn.addEventListener('click', () => { handleWebPlayButtonClick(btn, magnet, infoHash); }); return btn; } // 创建下载按钮 function createDownloadButton(magnet, infoHash) { const btn = document.createElement('button'); btn.textContent = '⬇ 下载'; Object.assign(btn.style, { marginLeft: '5px', padding: '2px 6px', background: '#4CAF50', color: 'white', border: 'none', borderRadius: '4px', cursor: 'pointer' }); btn.addEventListener('click', () => { handleDownloadButtonClick(btn, magnet, infoHash); }); return btn; } // 页面按钮添加 function addButtonsToHomePage() { const containers = document.querySelectorAll('.sk-col.res-name.word-wrap'); containers.forEach(container => { if (container.dataset.buttonAdded) return; const a = container.querySelector('a[data-clipboard-text]'); if (!a) return; const magnet = a.dataset.clipboardText; const match = magnet.match(/btih:([A-Za-z0-9]{32,40})/i); if (!match) return; const infoHash = match[1]; const os = detectOS(); if (os == 'Windows' || os == 'MacOS' || os == 'ipad') { const localPlayBtn = createLocalPlayButton(magnet, infoHash); container.appendChild(localPlayBtn); } const webPlayBtn = createWebPlayButton(magnet, infoHash); container.appendChild(webPlayBtn); const downloadBtn = createDownloadButton(magnet, infoHash); container.appendChild(downloadBtn); container.dataset.buttonAdded = '1'; }); } function addButtonsToBangumiPage() { const wrappers = document.querySelectorAll('.magnet-link-wrap'); wrappers.forEach(wrapper => { let magnetElement = wrapper.nextElementSibling; if (!magnetElement || !magnetElement.dataset || !magnetElement.dataset.clipboardText) return; if (magnetElement.dataset.buttonAdded) return; const magnet = magnetElement.dataset.clipboardText; const match = magnet.match(/btih:([A-Za-z0-9]{32,40})/i); if (!match) return; const infoHash = match[1]; const os = detectOS(); let localPlayBtn; if (os == 'Windows' || os == 'MacOS' || os == 'ipad') { localPlayBtn = createLocalPlayButton(magnet, infoHash); } const webPlayBtn = createWebPlayButton(magnet, infoHash); const downloadBtn = createDownloadButton(magnet, infoHash); if (os == 'Windows' || os == 'MacOS' || os == 'ipad') { magnetElement.parentNode.insertBefore(localPlayBtn, magnetElement.nextSibling); magnetElement.parentNode.insertBefore(webPlayBtn, localPlayBtn.nextSibling); magnetElement.parentNode.insertBefore(downloadBtn, webPlayBtn.nextSibling); } else { magnetElement.parentNode.insertBefore(webPlayBtn, magnetElement.nextSibling); magnetElement.parentNode.insertBefore(downloadBtn, webPlayBtn.nextSibling); } magnetElement.dataset.buttonAdded = '1'; }); } // 服务器信息显示 function addServerInfoToPage() { serverInfoDiv = document.createElement('div'); serverInfoDiv.textContent = `当前播放服务器: ${currentServer.name} (测试中...)`; Object.assign(serverInfoDiv.style, { position: 'fixed', bottom: '10px', right: '10px', background: 'rgba(0, 0, 0, 0.8)', color: '#fff', padding: '8px 12px', borderRadius: '6px', fontSize: '12px', zIndex: '9999', fontFamily: 'Arial, sans-serif', boxShadow: '0 2px 8px rgba(0,0,0,0.3)' }); document.body.appendChild(serverInfoDiv); } // 主函数和循环 function addPlayButtons() { if (isHomePage()) { addButtonsToHomePage(); } else if (isBangumiPage()) { addButtonsToBangumiPage(); } if (!mutationObserver) { mutationObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length || mutation.type === 'attributes') { if (isHomePage()) { addButtonsToHomePage(); } else if (isBangumiPage()) { addButtonsToBangumiPage(); } break; } } }); mutationObserver.observe(document.body, { childList: true, subtree: true }); } } // 初始化和清理 async function initialize() { // 如果配置了远程服务器从远程服务器拉取服务器信息 if (SERVER_LIST_URL && SERVER_LIST_URL != '') { SERVER_LIST = []; await loadServerListFromCache(); if (!SERVER_LIST || SERVER_LIST.length == 0) { try { await fetchServerList(); } catch (e) { console.error('获取服务器列表失败:', e); } } } try { // 加载保存的服务器配置 const serverIndex = await GM.getValue(STORAGE_KEY, DEFAULT_SERVER_INDEX); if (serverIndex >= 0 && serverIndex < SERVER_LIST.length) { currentServer = SERVER_LIST[serverIndex]; } else { currentServer = SERVER_LIST[DEFAULT_SERVER_INDEX]; } } catch (error) { console.error('加载配置失败:', error); currentServer = SERVER_LIST[DEFAULT_SERVER_INDEX]; } // 添加服务器信息显示 addServerInfoToPage(); // 测试当前服务器ping(仅一次) await testServerListPing(); // 注册(不可用)菜单命令 registerMenuCommands(); // 开始添加播放按钮 addPlayButtons(); console.log('蜜柑计划增强脚本初始化完成'); } // 页面卸载时清理资源 window.addEventListener('beforeunload', () => { Object.values(torrentIntervals).forEach(interval => { clearInterval(interval); }); torrentIntervals = {}; if (mutationObserver) { mutationObserver.disconnect(); mutationObserver = null; } console.log('脚本资源已清理'); }); // 启动脚本 initialize(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址