// ==UserScript== // @name 一键复制磁力链和推送到115离线 // @author wangzijian0@vip.qq.com // @description 目前支持BT4G/BTDig/BTSOW/SOBT/BTMulu/Nyaa/DMHY/CLM(磁力链搜索引擎)添加一键复制磁力链和推送到115网盘进行离线(推送离线任务需当前浏览器已登录115会员账号) // @version 1.0.9.20250423 // @icon https://github.githubassets.com/assets/mona-loading-default-c3c7aad1282f.gif // @include https://bt4gprx.com/* // @include https://btdig.com/* // @include https://www.btdig.com/* // @include https://sobt*.*/* // @include https://nyaa.si/* // @include https://btsow.*/* // @include https://cl*.cl*/* // @match https://*.btmulu.work/* // @match https://*.dmhy.org/* // @grant GM_setClipboard // @grant GM_notification // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @connect 115.com // @connect login.115.com // @connect * // @run-at document-end // @namespace https://greasyfork.org/users/1453515 // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/531685/%E4%B8%80%E9%94%AE%E5%A4%8D%E5%88%B6%E7%A3%81%E5%8A%9B%E9%93%BE%E5%92%8C%E6%8E%A8%E9%80%81%E5%88%B0115%E7%A6%BB%E7%BA%BF.user.js // @updateURL https://update.greasyfork.icu/scripts/531685/%E4%B8%80%E9%94%AE%E5%A4%8D%E5%88%B6%E7%A3%81%E5%8A%9B%E9%93%BE%E5%92%8C%E6%8E%A8%E9%80%81%E5%88%B0115%E7%A6%BB%E7%BA%BF.meta.js // ==/UserScript== (function() { 'use strict'; // 移动设备检测 const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); // 配置参数 const CONFIG = { notificationTimeout: isMobile ? 5000 : 3000, retryCount: 3, retryDelay: 1000, cookieRefreshInterval: 30 * 60 * 1000 // 30分钟刷新一次Cookie }; // 错误码映射 const ERROR_CODES = { 10008: '任务已存在,无需重复添加', 911: '需要账号验证,请确保已登录115会员账号', 990: '任务包含违规内容,无法添加', 991: '服务器繁忙,请稍后再试', 992: '离线下载配额已用完', 993: '当前账号无权使用离线下载功能', 994: '文件大小超过限制', 995: '不支持的链接类型', 996: '网络错误,请检查连接', 997: '服务器内部错误', 998: '请求超时', 999: '未知错误' }; // 初始化脚本 initScript(); function initScript() { // 添加Tampermonkey菜单命令 GM_registerMenuCommand("检查115登录状态", async () => { const isLoggedIn = await check115Login(true); showNotification('115状态', isLoggedIn ? '已登录' : '未登录'); if (!isLoggedIn) { setTimeout(() => { if (confirm('需要登录115网盘,是否进入115网盘登录页面?')) { window.open("https://115.com/?mode=login", "_blank"); } }, 500); } }); GM_registerMenuCommand("清除115登录状态", () => { GM_setValue('115_cookies', ''); GM_setValue('115_last_cookie_refresh', 0); showNotification('115状态', '已清除登录状态'); }); GM_registerMenuCommand("打开115网盘", () => window.open("https://115.com", "_blank")); // 设置定时刷新Cookie setInterval(() => { const lastRefresh = GM_getValue('115_last_cookie_refresh', 0); if (Date.now() - lastRefresh > CONFIG.cookieRefreshInterval) { check115Login(false); } }, 5 * 60 * 1000); // 每5分钟检查一次 // 监听页面变化 const observer = new MutationObserver(handleDomChanges); observer.observe(document, { childList: true, subtree: true }); // 初始添加按钮 addActionButtons(); } // DOM变化处理 function handleDomChanges(mutations) { for (const mutation of mutations) { if (mutation.addedNodes.length) { addActionButtons(); } } } // 添加操作按钮 function addActionButtons() { // BT4G网站处理 if (window.location.host.includes('bt4gprx.com')) { handleBT4GSite(); } /* ------------- CLM计划移除Start ------------- */ // CLM网站处理 else if (/cl[^.]+\.[^.]+\..+/.test(window.location.host)) { handleCLMSite(); } /* ------------- CLM计划移除End ------------- */ // 其他网站处理 else { handleCommonSites(); } /* ------------- CLM计划移除Start ------------- */ } // 处理CLM网站 function handleCLMSite() { // 处理磁力链部分 const magnetWrapper = document.querySelector('.Information_magnet_wrapper'); if (!magnetWrapper || magnetWrapper.dataset.buttonsAdded) return; // 标记已处理 magnetWrapper.dataset.buttonsAdded = true; // 获取磁力链接 const magnetLink = document.querySelector('.Information_magnet'); if (!magnetLink || !magnetLink.href.startsWith('magnet:')) return; // 创建按钮容器 const btnContainer = document.createElement('div'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.margin = '10px 0'; btnContainer.style.display = 'flex'; btnContainer.style.gap = '10px'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在磁力链后面 magnetLink.parentNode.insertBefore(btnContainer, magnetLink.nextSibling); /* ------------- CLM计划移除End ------------- */ } // 处理BT4G网站 function handleBT4GSite() { document.querySelectorAll('.card-body').forEach(cardBody => { if (cardBody.dataset.buttonsAdded) return; const magnetBtn = cardBody.querySelector('a[href*="downloadtorrentfile.com/hash/"]'); if (!magnetBtn) return; // 标记已处理 cardBody.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('div'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginRight = '10px'; // 添加按钮 btnContainer.appendChild(createBT4GCopyButton(magnetBtn)); btnContainer.appendChild(createBT4GOfflineButton(magnetBtn)); // 插入到DOM magnetBtn.parentNode.insertBefore(btnContainer, magnetBtn); }); } // 创建BT4G风格的复制按钮 function createBT4GCopyButton(element) { const btn = document.createElement('button'); btn.className = 'btn btn-sm copy-magnet-btn'; btn.innerHTML = '🔗 复制磁力链'; Object.assign(btn.style, { cursor: 'pointer', backgroundColor: '#000000', color: '#ffffff', border: '1px solid #000000', borderRadius: '6px', padding: isMobile ? '0.5rem 1rem' : '0.25rem 0.5rem', fontSize: isMobile ? '1rem' : '0.875rem', marginRight: '10px', transition: 'all 0.15s ease-in-out', fontWeight: '400', lineHeight: isMobile ? '1.2' : '2', textAlign: 'center', verticalAlign: 'middle', userSelect: 'none', // 移动端优化 touchAction: 'manipulation', minWidth: isMobile ? '120px' : 'auto', minHeight: isMobile ? '40px' : 'auto' }); btn.addEventListener('mouseenter', () => { btn.style.backgroundColor = '#333333'; btn.style.borderColor = '#333333'; }); btn.addEventListener('mouseleave', () => { btn.style.backgroundColor = '#000000'; btn.style.borderColor = '#000000'; }); // 触摸反馈 btn.addEventListener('touchstart', () => { btn.style.backgroundColor = '#333333'; btn.style.borderColor = '#333333'; }); btn.addEventListener('touchend', () => { btn.style.backgroundColor = '#000000'; btn.style.borderColor = '#000000'; }); const handleCopy = async (e) => { e.preventDefault(); e.stopPropagation(); const magnetLink = await extractMagnetLink(element); if (!magnetLink) return; try { GM_setClipboard(magnetLink, 'text'); // 移动端备用方案 if (isMobile && navigator.clipboard && navigator.clipboard.writeText) { try { await navigator.clipboard.writeText(magnetLink); } catch (clipboardError) { console.log('使用navigator.clipboard失败:', clipboardError); } } showNotification('磁力链已复制', magnetLink); // 按钮反馈效果 const originalHTML = btn.innerHTML; btn.innerHTML = '🔗 复制成功!'; btn.disabled = true; setTimeout(() => { btn.innerHTML = originalHTML; btn.disabled = false; }, 2000); } catch (error) { showNotification('复制失败', `请手动复制: ${magnetLink}`); } }; // 同时监听点击和触摸事件 btn.addEventListener('click', handleCopy); btn.addEventListener('touchend', handleCopy); return btn; } // 创建BT4G风格的115离线按钮 function createBT4GOfflineButton(element) { const btn = document.createElement('button'); btn.className = 'btn btn-sm offline-115-btn'; btn.innerHTML = ' 推送到115离线'; Object.assign(btn.style, { cursor: 'pointer', backgroundColor: '#1E50A2', color: '#fff', border: '1px solid #1a4580', borderRadius: '6px', padding: isMobile ? '0.5rem 1rem' : '0.25rem 0.5rem', fontSize: isMobile ? '1rem' : '0.875rem', transition: 'all 0.15s ease-in-out', fontWeight: '400', lineHeight: isMobile ? '1.2' : '2', textAlign: 'center', verticalAlign: 'middle', userSelect: 'none', // 移动端优化 touchAction: 'manipulation', minWidth: isMobile ? '140px' : 'auto', minHeight: isMobile ? '40px' : 'auto' }); btn.addEventListener('mouseenter', () => { btn.style.backgroundColor = '#1a4580'; btn.style.borderColor = '#163c70'; }); btn.addEventListener('mouseleave', () => { btn.style.backgroundColor = '#1E50A2'; btn.style.borderColor = '#1a4580'; }); // 触摸反馈 btn.addEventListener('touchstart', () => { btn.style.backgroundColor = '#1a4580'; btn.style.borderColor = '#163c70'; }); btn.addEventListener('touchend', () => { btn.style.backgroundColor = '#1E50A2'; btn.style.borderColor = '#1a4580'; }); const handleOffline = async (e) => { e.preventDefault(); e.stopPropagation(); const magnetLink = await extractMagnetLink(element); if (!magnetLink) return; await process115Offline(magnetLink); }; // 同时监听点击和触摸事件 btn.addEventListener('click', handleOffline); btn.addEventListener('touchend', handleOffline); return btn; } // 处理其他通用网站 function handleCommonSites() { // SOBT网站处理 if (/sobt[^.]+\..+/.test(window.location.host)) { handleSOBTSite(); } // BTDig网站处理 else if (window.location.host.endsWith('btdig.com')) { handleBTDigSite(); } // BTMulu网站处理 else if (window.location.host.endsWith('btmulu.work')) { handleBTMuluSite(); } // Nyaa网站处理 else if (window.location.host.includes('nyaa.si')) { handleNyaaSite(); } // DMHY网站处理 else if (window.location.host.includes('dmhy.org')) { handleDMHYSite(); } // BTSOW网站处理 else if (window.location.host.includes('btsow.pics') || window.location.host.includes('btsow.com')) { handleBTSOWSite(); } } // 处理BTSOW网站 function handleBTSOWSite() { // 处理搜索结果项 document.querySelectorAll('.data-list .row > a[href*="/magnet/detail/hash/"]').forEach(titleLink => { if (titleLink.dataset.buttonsAdded) return; // 标记已处理 titleLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '15px'; btnContainer.style.marginRight = '10px'; // 提取hash const hashMatch = titleLink.href.match(/\/hash\/([a-f0-9]+)/i); if (!hashMatch || !hashMatch[1]) return; const magnetLink = `magnet:?xt=urn:btih:${hashMatch[1]}`; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在标题下 titleLink.parentNode.insertBefore(btnContainer, titleLink.nextSibling); titleLink.closest('.row').appendChild(btnContainer); }); } // 处理SOBT网站 function handleSOBTSite() { // 处理搜索结果页 document.querySelectorAll('h3 > a[href^="/torrent/"]').forEach(titleLink => { if (titleLink.dataset.buttonsAdded) return; // 标记已处理 titleLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '10px'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(titleLink)); btnContainer.appendChild(createCommonOfflineButton(titleLink)); // 插入到DOM titleLink.parentNode.insertBefore(btnContainer, titleLink.nextSibling); }); // 处理详情页 document.querySelectorAll('.panel-body a[href^="magnet:"]').forEach(magnetLink => { if (magnetLink.dataset.buttonsAdded) return; // 标记已处理 magnetLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('div'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.margin = '10px 0'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM magnetLink.parentNode.insertBefore(btnContainer, magnetLink.nextSibling); }); } // 处理BTDig网站 function handleBTDigSite() { // 处理搜索结果项 document.querySelectorAll('.torrent_name > a').forEach(titleLink => { if (titleLink.dataset.buttonsAdded) return; // 标记已处理 titleLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginRight = '10px'; // 获取磁力链 const magnetLink = document.querySelector('.torrent_magnet a[href^="magnet:"]'); if (!magnetLink) return; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在标题前面 titleLink.parentNode.insertBefore(btnContainer, titleLink); }); } // 处理BTMulu网站 function handleBTMuluSite() { // 处理搜索结果项 document.querySelectorAll('article.item a[href^="/hash/"]').forEach(titleLink => { if (titleLink.dataset.buttonsAdded) return; // 标记已处理 titleLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '10px'; // 提取hash const hashMatch = titleLink.href.match(/\/hash\/([a-f0-9]+)\.html$/i); if (!hashMatch || !hashMatch[1]) return; const magnetLink = `magnet:?xt=urn:btih:${hashMatch[1]}`; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在标题后面 titleLink.parentNode.insertBefore(btnContainer, titleLink.nextSibling); }); } // 处理Nyaa网站 function handleNyaaSite() { // 处理搜索结果项 document.querySelectorAll('td.text-center a[href^="magnet:"]').forEach(magnetLink => { if (magnetLink.dataset.buttonsAdded) return; // 标记已处理 magnetLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '10px'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在磁力链图标后面 magnetLink.parentNode.insertBefore(btnContainer, magnetLink.nextSibling); }); } // 处理DMHY网站 function handleDMHYSite() { // 调整磁力链列宽度为18% const magnetHeader = document.querySelector('#topic_list th:nth-child(4)'); if (magnetHeader) { magnetHeader.style.width = '18%'; } // 处理搜索结果项 document.querySelectorAll('a.download-arrow.arrow-magnet').forEach(magnetLink => { if (magnetLink.dataset.buttonsAdded) return; // 标记已处理 magnetLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '5px'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在磁力链图标前面 magnetLink.parentNode.insertBefore(btnContainer, magnetLink); }); // 处理详情页的磁力链接 document.querySelectorAll('#tabs-1 a.magnet, #tabs-1 a#magnet2').forEach(magnetLink => { if (magnetLink.dataset.buttonsAdded) return; // 标记已处理 magnetLink.dataset.buttonsAdded = true; // 创建按钮容器 const btnContainer = document.createElement('span'); btnContainer.className = 'magnet-action-buttons'; btnContainer.style.display = 'inline-block'; btnContainer.style.marginLeft = '5px'; // 添加按钮 btnContainer.appendChild(createCommonCopyButton(magnetLink)); btnContainer.appendChild(createCommonOfflineButton(magnetLink)); // 插入到DOM - 放在磁力链后面 magnetLink.parentNode.insertBefore(btnContainer, magnetLink.nextSibling); }); } // 创建通用复制按钮 function createCommonCopyButton(element) { const btn = document.createElement('button'); btn.className = 'btn btn-info me-2 copy-magnet-btn'; btn.innerHTML = '🔗 复制'; Object.assign(btn.style, { cursor: 'pointer', backgroundColor: '#000000', color: '#ffffff', border: '1px solid #000000', borderRadius: '4px', padding: isMobile ? '8px 12px' : '4px 8px', fontSize: isMobile ? '14px' : '12px', marginRight: '5px', transition: 'all 0.15s ease-in-out', fontWeight: '400', lineHeight: '1.5', verticalAlign: 'middle', // 移动端优化 touchAction: 'manipulation', minWidth: isMobile ? '120px' : 'auto', minHeight: isMobile ? '40px' : 'auto' }); btn.addEventListener('mouseenter', () => { btn.style.backgroundColor = '#333333'; btn.style.borderColor = '#333333'; }); btn.addEventListener('mouseleave', () => { btn.style.backgroundColor = '#000000'; btn.style.borderColor = '#000000'; }); // 触摸反馈 btn.addEventListener('touchstart', () => { btn.style.backgroundColor = '#333333'; btn.style.borderColor = '#333333'; }); btn.addEventListener('touchend', () => { btn.style.backgroundColor = '#000000'; btn.style.borderColor = '#000000'; }); const handleCopy = async (e) => { e.preventDefault(); e.stopPropagation(); const magnetLink = await extractMagnetLink(element); if (!magnetLink) return; try { GM_setClipboard(magnetLink, 'text'); // 移动端备用方案 if (isMobile && navigator.clipboard && navigator.clipboard.writeText) { try { await navigator.clipboard.writeText(magnetLink); } catch (clipboardError) { console.log('使用navigator.clipboard失败:', clipboardError); } } showNotification('磁力链已复制', magnetLink); // 按钮反馈效果 const originalHTML = btn.innerHTML; btn.innerHTML = '🔗 完成'; btn.disabled = true; setTimeout(() => { btn.innerHTML = originalHTML; btn.disabled = false; }, 2000); } catch (error) { showNotification('复制失败', `请手动复制: ${magnetLink}`); } }; // 同时监听点击和触摸事件 btn.addEventListener('click', handleCopy); btn.addEventListener('touchend', handleCopy); return btn; } // 创建通用115离线按钮 function createCommonOfflineButton(element) { const btn = document.createElement('button'); btn.className = 'btn btn-danger me-2 offline-115-btn'; btn.innerHTML = ' 115离线'; Object.assign(btn.style, { cursor: 'pointer', backgroundColor: '#1E50A2', color: '#fff', border: '1px solid #1a4580', borderRadius: '4px', padding: isMobile ? '8px 12px' : '4px 8px', fontSize: isMobile ? '14px' : '12px', transition: 'all 0.15s ease-in-out', fontWeight: '400', lineHeight: '1.5', verticalAlign: 'middle', // 移动端优化 touchAction: 'manipulation', minWidth: isMobile ? '140px' : 'auto', minHeight: isMobile ? '40px' : 'auto' }); btn.addEventListener('mouseenter', () => { btn.style.backgroundColor = '#1a4580'; btn.style.borderColor = '#163c70'; }); btn.addEventListener('mouseleave', () => { btn.style.backgroundColor = '#1E50A2'; btn.style.borderColor = '#1a4580'; }); // 触摸反馈 btn.addEventListener('touchstart', () => { btn.style.backgroundColor = '#1a4580'; btn.style.borderColor = '#163c70'; }); btn.addEventListener('touchend', () => { btn.style.backgroundColor = '#1E50A2'; btn.style.borderColor = '#1a4580'; }); const handleOffline = async (e) => { e.preventDefault(); e.stopPropagation(); const magnetLink = await extractMagnetLink(element); if (!magnetLink) return; await process115Offline(magnetLink); }; // 同时监听点击和触摸事件 btn.addEventListener('click', handleOffline); btn.addEventListener('touchend', handleOffline); return btn; } // 从元素提取磁力链 async function extractMagnetLink(element) { try { // 如果是SOBT网站的标题链 if (element.href && element.href.includes('/torrent/')) { const hashMatch = element.href.match(/\/torrent\/([a-f0-9]+)\.html$/i); if (hashMatch && hashMatch[1]) { return `magnet:?xt=urn:btih:${hashMatch[1]}`; } } // 如果是BT4G网站的磁力链 else if (element.href && element.href.includes('downloadtorrentfile.com/hash/')) { const hashMatch = element.href.match(/hash\/([a-f0-9]+)/i); if (hashMatch && hashMatch[1]) { return `magnet:?xt=urn:btih:${hashMatch[1]}`; } } // 如果是BTMulu网站的标题链 else if (element.href && element.href.includes('/hash/')) { const hashMatch = element.href.match(/\/hash\/([a-f0-9]+)\.html$/i); if (hashMatch && hashMatch[1]) { return `magnet:?xt=urn:btih:${hashMatch[1]}`; } } // 如果是BTSOW网站的标题链 else if (element.href && element.href.includes('/magnet/detail/hash/')) { const hashMatch = element.href.match(/\/hash\/([a-f0-9]+)/i); if (hashMatch && hashMatch[1]) { return `magnet:?xt=urn:btih:${hashMatch[1]}`; } } // 如果是Nyaa/DMHY网站或直接磁力链 else if (element.href && element.href.startsWith('magnet:')) { return element.href; } // 如果是字符串直接返回 else if (typeof element === 'string' && element.startsWith('magnet:')) { return element; } throw new Error('无法提取磁力链Hash'); } catch (error) { showNotification('错误', error.message); return null; } } // 检查115登录状态 async function check115Login(forceCheck = false) { try { // 如果有强制检查标志或者Cookie为空或者超过刷新间隔,则重新获取 const lastRefresh = GM_getValue('115_last_cookie_refresh', 0); const currentCookies = GM_getValue('115_cookies', ''); if (!forceCheck && currentCookies && Date.now() - lastRefresh < CONFIG.cookieRefreshInterval) { return true; } // 尝试获取当前有效的Cookie const cookies = await getCurrent115Cookies(); if (!cookies) { GM_setValue('115_cookies', ''); GM_setValue('115_last_cookie_refresh', 0); return false; } // 验证Cookie是否有效 const isValid = await validate115Cookies(cookies); if (isValid) { GM_setValue('115_cookies', cookies); GM_setValue('115_last_cookie_refresh', Date.now()); return true; } GM_setValue('115_cookies', ''); GM_setValue('115_last_cookie_refresh', 0); return false; } catch (error) { console.error('检查登录状态失败:', error); return false; } } // 获取当前有效的115 Cookie function getCurrent115Cookies() { return new Promise((resolve) => { GM_xmlhttpRequest({ url: 'https://115.com/', method: 'GET', anonymous: true, // 不发送现有Cookie onload: function(response) { // 从响应头获取Cookie const cookieHeader = response.responseHeaders .split('\n') .find(row => row.toLowerCase().startsWith('set-cookie:')); if (cookieHeader) { const cookies = cookieHeader.replace(/^set-cookie:\s*/i, '').split(';')[0]; resolve(cookies); } else { // 检查是否重定向到登录页面 if (response.finalUrl.includes('login.115.com')) { resolve(''); } else { // 尝试使用之前存储的Cookie const savedCookies = GM_getValue('115_cookies', ''); resolve(savedCookies); } } }, onerror: () => resolve('') }); }); } // 验证Cookie是否有效 function validate115Cookies(cookies) { return new Promise((resolve) => { GM_xmlhttpRequest({ url: 'https://115.com/web/lixian/', method: 'GET', headers: { 'Cookie': cookies }, onload: function(response) { // 检查是否重定向到登录页面 if (response.finalUrl.includes('login.115.com')) { resolve(false); } else { resolve(true); } }, onerror: () => resolve(false) }); }); } // 115离线下载处理流程 async function process115Offline(magnetLink) { const notificationId = Date.now(); try { // 步骤1:检查登录状态 showNotification('115离线', '正在检查登录状态...', notificationId); const isLoggedIn = await check115Login(true); if (!isLoggedIn) { throw new Error('请先登录115网盘'); } // 步骤2:提交离线任务 showNotification('115离线', '正在提交离线任务...', notificationId); const result = await submit115OfflineTask(magnetLink); // 处理结果 handleOfflineResult(result); } catch (error) { showNotification('115离线失败', error.message); // 如果是未登录错误,显示登录提示 if (error.message.includes('登录')) { setTimeout(() => { if (confirm('需要登录115网盘,是否进入115网盘登录页面?')) { window.open('https://115.com/?mode=login', '_blank'); } }, 500); } } finally { // 关闭进度通知 GM_notification({ id: notificationId, done: true }); } } // 提交115离线任务 async function submit115OfflineTask(magnetLink) { const cookies = GM_getValue('115_cookies', ''); if (!cookies) { throw new Error('未检测到有效的登录状态'); } const response = await fetch115Api( `https://115.com/web/lixian/?ct=lixian&ac=add_task_url&url=${encodeURIComponent(magnetLink)}`, { headers: { 'Cookie': cookies } } ); return tryParseJson(response); } // 处理离线结果 function handleOfflineResult(result) { if (!result) { throw new Error('无效的响应'); } if (result.state) { showNotification('115离线成功', '任务已成功添加到离线下载列表'); return; } const errorMsg = ERROR_CODES[result.errcode] || result.error_msg || '未知错误'; throw new Error(errorMsg); } // 通用请求函数 function fetch115Api(url, options = {}) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ url: url, method: options.method || 'GET', headers: { 'User-Agent': navigator.userAgent, 'Origin': 'https://115.com', ...(options.headers || {}) }, data: options.body, onload: function(response) { if (response.status >= 200 && response.status < 300) { resolve(response.responseText); } else { reject(new Error(`请求失败: ${response.status}`)); } }, onerror: reject }); }); } // 尝试解析JSON function tryParseJson(text) { try { return JSON.parse(text); } catch (e) { return null; } } // 显示通知 function showNotification(title, text, id = null) { GM_notification({ title: title, text: text, timeout: CONFIG.notificationTimeout, ...(id ? { id } : {}) }); } })();