您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
复制和下载按旁添加新按钮,离线路径,按钮名字可使用ui选择自定义,按钮2可开关,使用115web接口实现,支持并适配移动端与pc端
// ==UserScript== // @name JavDB 推送115离线 // @namespace javdb.button.empty // @version 0.4 // @description 复制和下载按旁添加新按钮,离线路径,按钮名字可使用ui选择自定义,按钮2可开关,使用115web接口实现,支持并适配移动端与pc端 // @match https://javdb561.com/* // @match https://javdb.com/* // @icon https://javdb561.com/favicon.ico // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @run-at document-end // @author 楠 // @license MIT // ==/UserScript== (function () { 'use strict'; const TARGET_CLASS = "x-empty-btn"; let btn1Name = GM_getValue('btn1_name', '按钮1'); let btn2Name = GM_getValue('btn2_name', '按钮2'); let btn2Enabled = GM_getValue('btn2_enabled', false); let btn1Cid = GM_getValue('btn1_cid', ''); let btn2Cid = GM_getValue('btn2_cid', ''); let cookie = GM_getValue('cookie', ''); function handleMagnetLink(magnet, targetCid, buttonName) { console.log(`[${buttonName}] 捕获磁力链接:`, magnet); console.log(`[${buttonName}] 目标CID:`, targetCid); if (!targetCid) { showToast(`❌ 请先为【${buttonName}】设置目标文件夹`, 3000); showSettingsModal(); return; } if (!cookie) { showToast('❌ 请先设置115 Cookie', 3000); showSettingsModal(); return; } GM_xmlhttpRequest({ method: "POST", url: "https://115.com/web/lixian/?ct=lixian&ac=add_task_url", headers: { "Cookie": cookie, "Content-Type": "application/x-www-form-urlencoded", "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://115.com/" }, data: `url=${encodeURIComponent(magnet)}&wp_path_id=${encodeURIComponent(targetCid)}`, onload: function(response) { try { const result = JSON.parse(response.responseText); console.log('115 API响应:', result); if (result.state === true) { showToast(`✅ 【${buttonName}】离线任务提交成功`); } else if (result.error_msg && result.error_msg.includes("任务已存在")) { showToast(`⚠️ 任务已存在,请勿输入重复的链接地址`, 3000, 'warning'); } else { showToast(`❌ 【${buttonName}】提交失败: ${result.error_msg || '未知错误'}`); } } catch (error) { console.error('解析响应失败:', error); showToast('❌ 解析115响应失败'); } }, onerror: function(error) { console.error('115请求错误:', error); showToast('❌ 115请求错误'); } }); } function generateButtonsHTML() { let html = `<button class="${TARGET_CLASS} button is-small x-un-hover is-primary">${btn1Name}</button>`; if (btn2Enabled) { html += `<button class="${TARGET_CLASS} button is-small x-un-hover is-link">${btn2Name}</button>`; } return html; } function insertToMagnets() { const magnetsNode = document.querySelector("#magnets-content"); if (!magnetsNode) return; magnetsNode.querySelectorAll(".item.columns").forEach((node) => { const btns = node.querySelector(".buttons.column"); if (btns && !btns.querySelector(`.${TARGET_CLASS}`)) { btns.innerHTML += generateButtonsHTML(); const btn1 = btns.querySelector(`.${TARGET_CLASS}.is-primary`); if (btn1) { btn1.onclick = () => { const aTag = node.querySelector('a[href^="magnet:?"]'); if (aTag) { handleMagnetLink(aTag.getAttribute('href'), btn1Cid, btn1Name); } else { showToast("❌ 未找到磁力链接"); } }; } if (btn2Enabled) { const btn2 = btns.querySelector(`.${TARGET_CLASS}.is-link`); if (btn2) { btn2.onclick = () => { const aTag = node.querySelector('a[href^="magnet:?"]'); if (aTag) { handleMagnetLink(aTag.getAttribute('href'), btn2Cid, btn2Name); } else { showToast("❌ 未找到磁力链接"); } }; } } } }); } insertToMagnets(); const observer = new MutationObserver(insertToMagnets); observer.observe(document.body, { childList: true, subtree: true }); function showToast(message, duration = 2500, type = 'default') { const toast = document.createElement('div'); let bgGradient; if (type === 'warning') { bgGradient = 'linear-gradient(135deg, #ff9800, #f57c00)'; } else if (message.includes('❌') || message.includes('失败') || message.includes('错误')) { bgGradient = 'linear-gradient(135deg, #ff5252, #b71c1c)'; } else if (message.includes('✅') || message.includes('成功')) { bgGradient = 'linear-gradient(135deg, #4CAF50, #2E7D32)'; } else { bgGradient = 'linear-gradient(135deg, #2196F3, #1976D2)'; } Object.assign(toast.style, { position: 'fixed', top: '110px', right: '20px', padding: '16px 24px', background: bgGradient, color: '#fff', borderRadius: '12px', boxShadow: '0 6px 20px rgba(0,0,0,0.2)', fontSize: '14px', fontWeight: '500', opacity: '0', transform: 'translateX(100%) scale(0.9)', transition: 'all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55)', zIndex: 10000, maxWidth: '300px', backdropFilter: 'blur(10px)', border: '1px solid rgba(255,255,255,0.1)', overflow: 'hidden' }); const progressBar = document.createElement('div'); Object.assign(progressBar.style, { position: 'absolute', bottom: '0', left: '0', height: '3px', background: 'linear-gradient(90deg, rgba(255,255,255,0.5), rgba(255,255,255,0.8))', width: '100%', transform: 'scaleX(1)', transformOrigin: 'left center', transition: 'transform linear', borderRadius: '0 0 12px 12px' }); toast.appendChild(progressBar); const icon = document.createElement('span'); if (type === 'warning') { icon.innerHTML = '⚠️'; } else if (message.includes('❌') || message.includes('失败') || message.includes('错误')) { icon.innerHTML = '❌'; } else if (message.includes('✅') || message.includes('成功')) { icon.innerHTML = '✓'; } else { icon.innerHTML = 'ℹ️'; } Object.assign(icon.style, { display: 'inline-block', marginRight: '10px', fontSize: '16px', fontWeight: 'bold', verticalAlign: 'middle' }); const textSpan = document.createElement('span'); textSpan.textContent = message; textSpan.style.verticalAlign = 'middle'; toast.appendChild(icon); toast.appendChild(textSpan); document.body.appendChild(toast); requestAnimationFrame(() => { toast.style.opacity = '1'; toast.style.transform = 'translateX(0) scale(1)'; progressBar.style.transition = `transform ${duration}ms linear`; progressBar.style.transform = 'scaleX(0)'; }); setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(100%) scale(0.9)'; setTimeout(() => { if (toast.parentNode) toast.parentNode.removeChild(toast); }, 500); }, duration); } async function getFolders(cid = 0) { if (!cookie) { showToast('请先设置Cookie'); return []; } try { const response = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "GET", url: `https://webapi.115.com/files?aid=1&cid=${cid}&show_dir=1`, headers: { "Cookie": cookie, "User-Agent": "Mozilla/5.0" }, onload: resolve, onerror: reject }); }); const data = JSON.parse(response.responseText); if (data.state && data.data) { return data.data.filter(item => item.fl && item.fl.length === 0) .map(item => ({ name: item.n, cid: item.cid })); } return []; } catch (error) { console.error('获取文件夹列表失败:', error); showToast('获取文件夹列表失败'); return []; } } function showSettingsModal() { if (document.querySelector('#tm-settings-modal')) return; const overlay = document.createElement('div'); overlay.id = 'tm-settings-modal'; Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', background: 'rgba(0,0,0,0.5)', zIndex: 10001, display: 'flex', justifyContent: 'center', alignItems: 'center' }); const modal = document.createElement('div'); Object.assign(modal.style, { background: '#fff', padding: '20px 25px', borderRadius: '10px', width: '500px', boxShadow: '0 6px 20px rgba(0,0,0,0.3)', fontFamily: 'Arial, sans-serif', maxHeight: '80vh', overflowY: 'auto' }); modal.innerHTML = ` <h3 style="margin-top:0;margin-bottom:15px;color:#333">115 设置</h3> <div style="margin-bottom:10px;"> <label style="display:block;margin-bottom:5px;color:#555;">Cookie:</label> <div style="display:flex;align-items:center;gap:5px;"> <input id="tm-cookie-input" type="password" value="${cookie}" style="flex:1;padding:6px;border:1px solid #ccc;border-radius:4px;"> <button id="tm-toggle-cookie" style="padding:6px 10px;border:1px solid #ccc;border-radius:4px;background:#f5f5f5;cursor:pointer;">显示</button> </div> </div> <div style="margin-bottom:15px;padding:10px;background:#f9f9f9;border-radius:5px;"> <h4 style="margin-top:0;color:#333;border-bottom:1px solid #eee;padding-bottom:5px;">${btn1Name} 设置</h4> <div style="margin-bottom:10px;"> <label style="display:block;margin-bottom:5px;color:#555;">按钮名称:</label> <input id="tm-btn1-name" type="text" value="${btn1Name}" style="width:100%;padding:6px;border:1px solid #ccc;border-radius:4px;"> </div> <div style="margin-bottom:10px;"> <label style="display:block;margin-bottom:5px;color:#555;">目标文件夹 CID:</label> <div style="display:flex;gap:10px;"> <input id="tm-btn1-cid-input" type="text" value="${btn1Cid}" style="flex:1;padding:6px;border:1px solid #ccc;border-radius:4px;" placeholder="未设置"> <button id="tm-btn1-browse-folders" style="padding:6px 12px;border:none;border-radius:4px;background:#2196F3;color:#fff;cursor:pointer;">浏览文件夹</button> </div> </div> </div> <div style="margin-bottom:15px;padding:10px;background:#f9f9f9;border-radius:5px;"> <h4 style="margin-top:0;color:#333;border-bottom:1px solid #eee;padding-bottom:5px;">${btn2Name} 设置</h4> <div style="margin-bottom:10px;"> <label style="display:block;margin-bottom:5px;color:#555;">按钮名称:</label> <input id="tm-btn2-name" type="text" value="${btn2Name}" style="width:100%;padding:6px;border:1px solid #ccc;border-radius:4px;"> </div> <div style="margin-bottom:10px;"> <label style="display:block;margin-bottom:5px;color:#555;">目标文件夹 CID:</label> <div style="display:flex;gap:10px;"> <input id="tm-btn2-cid-input" type="text" value="${btn2Cid}" style="flex:1;padding:6px;border:1px solid #ccc;border-radius:4px;" placeholder="未设置"> <button id="tm-btn2-browse-folders" style="padding:6px 12px;border:none;border-radius:4px;background:#2196F3;color:#fff;cursor:pointer;">浏览文件夹</button> </div> </div> <label style="display:inline-flex;align-items:center;"> <input type="checkbox" id="tm-btn2-enabled" ${btn2Enabled ? 'checked' : ''} style="margin-right:5px;"> 显示按钮2 </label> </div> <div style="text-align:right;"> <button id="tm-settings-cancel" style="margin-right:10px;padding:6px 12px;border:none;border-radius:4px;background:#ccc;color:#fff;cursor:pointer;">取消</button> <button id="tm-settings-save" style="padding:6px 12px;border:none;border-radius:4px;background:#4CAF50;color:#fff;cursor:pointer;">保存</button> </div> `; overlay.appendChild(modal); document.body.appendChild(overlay); const cookieInput = overlay.querySelector('#tm-cookie-input'); const toggleButton = overlay.querySelector('#tm-toggle-cookie'); toggleButton.addEventListener('click', function() { if (cookieInput.type === 'password') { cookieInput.type = 'text'; toggleButton.textContent = '隐藏'; } else { cookieInput.type = 'password'; toggleButton.textContent = '显示'; } }); overlay.querySelector('#tm-btn1-browse-folders').onclick = () => { const cookieValue = document.querySelector('#tm-cookie-input').value.trim(); if (!cookieValue) { showToast('请先设置Cookie'); return; } GM_setValue('cookie', cookieValue); cookie = cookieValue; showFolderBrowser('btn1'); }; overlay.querySelector('#tm-btn2-browse-folders').onclick = () => { const cookieValue = document.querySelector('#tm-cookie-input').value.trim(); if (!cookieValue) { showToast('请先设置Cookie'); return; } GM_setValue('cookie', cookieValue); cookie = cookieValue; showFolderBrowser('btn2'); }; overlay.querySelector('#tm-settings-cancel').onclick = () => overlay.remove(); overlay.querySelector('#tm-settings-save').onclick = () => { const newBtn1Name = document.querySelector('#tm-btn1-name').value.trim(); const newBtn2Name = document.querySelector('#tm-btn2-name').value.trim(); const newBtn1Cid = document.querySelector('#tm-btn1-cid-input').value.trim(); const newBtn2Cid = document.querySelector('#tm-btn2-cid-input').value.trim(); const newBtn2Enabled = document.querySelector('#tm-btn2-enabled').checked; const cookieValue = document.querySelector('#tm-cookie-input').value.trim(); if (newBtn1Cid && newBtn2Cid && newBtn1Cid === newBtn2Cid && newBtn2Enabled) { showToast('❌ 两个按钮的目标文件夹不能相同', 3000); return; } GM_setValue('cookie', cookieValue); GM_setValue('btn1_name', newBtn1Name); GM_setValue('btn2_name', newBtn2Name); GM_setValue('btn1_cid', newBtn1Cid); GM_setValue('btn2_cid', newBtn2Cid); GM_setValue('btn2_enabled', newBtn2Enabled); btn1Name = newBtn1Name; btn2Name = newBtn2Name; btn1Cid = newBtn1Cid; btn2Cid = newBtn2Cid; btn2Enabled = newBtn2Enabled; cookie = cookieValue; showToast('✅ 设置已保存,按钮已更新'); document.querySelectorAll(`.${TARGET_CLASS}`).forEach(btn => btn.remove()); insertToMagnets(); overlay.remove(); }; } async function showFolderBrowser(buttonType) { if (document.querySelector('#tm-folder-browser')) return; const overlay = document.createElement('div'); overlay.id = 'tm-folder-browser'; Object.assign(overlay.style, { position: 'fixed', top: '0', left: '0', width: '100%', height: '100%', background: 'rgba(0,0,0,0.5)', zIndex: 10002, display: 'flex', justifyContent: 'center', alignItems: 'center' }); const modal = document.createElement('div'); Object.assign(modal.style, { background: '#fff', padding: '20px', borderRadius: '10px', width: '500px', maxHeight: '80vh', boxShadow: '0 6px 20px rgba(0,0,0,0.3)', fontFamily: 'Arial, sans-serif', display: 'flex', flexDirection: 'column' }); modal.innerHTML = ` <h3 style="margin-top:0;margin-bottom:15px;color:#333">浏览文件夹 - ${buttonType === 'btn1' ? btn1Name : btn2Name}</h3> <div id="tm-current-path" style="margin-bottom:10px;padding:8px;background:#f5f5f5;border-radius:4px;">根目录</div> <div id="tm-folders-list" style="flex:1;overflow-y:auto;margin-bottom:15px;min-height:200px;"> <div style="text-align:center;padding:40px 0;">加载中...</div> </div> <div style="display:flex;justify-content:space-between;"> <button id="tm-folder-back" style="padding:6px 12px;border:none;border-radius:4px;background:#ccc;color:#fff;cursor:pointer;">返回上级</button> <button id="tm-folder-cancel" style="padding:6px 12px;border:none;border-radius:4px;background:#ccc;color:#fff;cursor:pointer;">取消</button> <button id="tm-folder-select" style="padding:6px 12px;border:none;border-radius:4px;background:#4CAF50;color:#fff;cursor:pointer;">选择当前文件夹</button> </div> `; overlay.appendChild(modal); document.body.appendChild(overlay); let currentCid = 0, currentPath = ["根目录"], cidStack = [], pathStack = []; const targetButtonType = buttonType; async function loadFolders(cid = 0) { const foldersList = document.getElementById('tm-folders-list'); foldersList.innerHTML = '<div style="text-align:center;padding:40px 0;">加载中...</div>'; const folders = await getFolders(cid); if (!folders.length) { foldersList.innerHTML = '<div style="text-align:center;padding:40px 0;color:#999;">该目录下没有文件夹</div>'; return; } foldersList.innerHTML = ''; folders.forEach(folder => { const folderItem = document.createElement('div'); folderItem.className = 'tm-folder-item'; folderItem.style.padding = '10px'; folderItem.style.borderBottom = '1px solid #eee'; folderItem.style.cursor = 'pointer'; folderItem.style.display = 'flex'; folderItem.style.justifyContent = 'space-between'; folderItem.style.alignItems = 'center'; folderItem.innerHTML = ` <span>${folder.name}</span> <span style="color:#999;font-size:12px;">CID: ${folder.cid}</span> `; folderItem.onclick = () => { cidStack.push(currentCid); pathStack.push([...currentPath]); currentCid = folder.cid; currentPath.push(folder.name); updatePathDisplay(); loadFolders(currentCid); }; foldersList.appendChild(folderItem); }); } function updatePathDisplay() { document.getElementById('tm-current-path').textContent = currentPath.join(' / '); } document.getElementById('tm-folder-back').onclick = () => { if (cidStack.length > 0) { currentCid = cidStack.pop(); currentPath = pathStack.pop(); updatePathDisplay(); loadFolders(currentCid); } }; document.getElementById('tm-folder-cancel').onclick = () => overlay.remove(); document.getElementById('tm-folder-select').onclick = () => { if (currentCid !== 0) { const cidInput = document.querySelector(`#tm-${targetButtonType}-cid-input`); if (cidInput) { cidInput.value = currentCid; const otherButtonType = targetButtonType === 'btn1' ? 'btn2' : 'btn1'; const otherCidInput = document.querySelector(`#tm-${otherButtonType}-cid-input`); const otherButtonEnabled = document.querySelector('#tm-btn2-enabled') ? document.querySelector('#tm-btn2-enabled').checked : false; if (otherCidInput && otherCidInput.value === cidInput.value && otherButtonEnabled) { showToast('⚠️ 注意: 两个按钮的目标文件夹相同', 3000, 'warning'); } } showToast(`已选择: ${currentPath.join(' / ')}`); } overlay.remove(); }; loadFolders(currentCid); updatePathDisplay(); } function addSettingsButton() { if (document.querySelector('#tm-settings-btn')) return; const btn = document.createElement('div'); btn.id = 'tm-settings-btn'; btn.textContent = '⚙️ 115设置'; Object.assign(btn.style, { position: 'fixed', bottom: '25px', right: '10px', backgroundColor: '#2196F3', color: '#fff', padding: '8px 12px', borderRadius: '8px', cursor: 'pointer', zIndex: 10000, fontWeight: 'bold', boxShadow: '0 4px 12px rgba(0,0,0,0.2)' }); btn.onclick = showSettingsModal; document.body.appendChild(btn); } addSettingsButton(); })();