Last active
October 1, 2025 14:35
-
-
Save helloint/f1e26da41683e6b5a11d7f490d4e425e to your computer and use it in GitHub Desktop.
NAS迅雷新建任务自动化
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ==UserScript== | |
| // @name NAS迅雷新建任务自动化 | |
| // @namespace https://helloint.com/ | |
| // @version 1.2 | |
| // @description 新建任务时自动完成下列操作:(1)自动设置目录,(2)去除广告前缀,(3)取消勾选广告文件 | |
| // @downloadURL https://gist.githubusercontent.com/helloint/f1e26da41683e6b5a11d7f490d4e425e/raw/xunlei-nas-assist.user.js | |
| // @author Wayne Mao | |
| // @match https://*.synology.me:*/webman/3rdparty/pan-xunlei-com/index.cgi* | |
| // @icon https://www.google.com/s2/favicons?sz=64&domain=web.app | |
| // @grant none | |
| // ==/UserScript== | |
| (function() { | |
| 'use strict'; | |
| const customFolderPath = '/volume2/Download'; // 默认选择的目录是“/迅雷/下载/”, 如果你用的不是默认目录,可以在这里配置。如果不需要改,可以留空'' | |
| let inProcess = false; | |
| let hasError = false; | |
| async function detect() { | |
| if (inProcess) return; | |
| const layer0 = findOverlay('添加链接'); | |
| if (!layer0) return; | |
| inProcess = true; | |
| const layer0CloseBtn = layer0.querySelector('.el-dialog__headerbtn'); | |
| layer0CloseBtn.addEventListener('click', () => { | |
| inProcess = false; | |
| }); | |
| const folderPaths = customFolderPath.split('/').slice(1); | |
| if (folderPaths.length > 0) { | |
| const changeLocationBtn = layer0.querySelector('.icon-a-xuanzemulu3x[role="button"]'); | |
| changeLocationBtn.click(); | |
| const layer1 = await waitFor(() => findOverlay('选择下载目录')); | |
| // 从【全部磁盘目录】下查找 | |
| for (const folderName of folderPaths) { | |
| const targetElement = await waitFor(() => Array.from(layer1.querySelectorAll('a.file_title')).find( | |
| el => el.textContent.trim().toLowerCase() === folderName.toLowerCase() | |
| )); | |
| if (targetElement) { | |
| targetElement.click(); | |
| } else { | |
| alert(`目录没找到: ${folderName}`); | |
| return; | |
| } | |
| } | |
| const confirmLocationBtn = await waitFor(() => Array.from(layer1.querySelectorAll('button.cinema__button')).find( | |
| el => el.textContent.trim().toLowerCase() === '确定' | |
| )); | |
| confirmLocationBtn.click(); | |
| } | |
| const taskParseBtn = await waitFor(() => Array.from(layer0.querySelectorAll('button.task-parse-btn')).find( | |
| el => el.textContent.trim().toLowerCase() === '确定')); | |
| await waitAndClick(taskParseBtn); | |
| const layer2 = await waitFor(() => findOverlay('新建下载任务')); | |
| const layer2CloseBtn = layer2.querySelector('.el-dialog__headerbtn'); | |
| layer2CloseBtn.addEventListener('click', () => { | |
| if (!hasError) { | |
| inProcess = false; | |
| } | |
| }); | |
| const fileSizeLabel = layer2.querySelector('.task-dialog__header').querySelector('.size'); | |
| if (fileSizeLabel.textContent === '0 B') { | |
| // 空链,直接中断返回。 | |
| hasError = true; | |
| return; | |
| } | |
| // 去掉下载目录名前面的广告前缀 | |
| const labelText = layer2.querySelector('.label_txt'); | |
| let cleanedText = labelText.textContent; | |
| if (cleanedText.startsWith("【")) { | |
| const endIndex = cleanedText.indexOf("】") + 1; | |
| cleanedText = cleanedText.slice(endIndex); | |
| } | |
| const editTitleBtn = layer2.querySelector('i.icon-write[role="button"]'); | |
| editTitleBtn.click(); | |
| const labelTitleTextArea = await waitFor(() => layer2.querySelector('textarea.el-textarea__inner')); | |
| labelTitleTextArea.value = cleanedText; | |
| labelTitleTextArea.dispatchEvent(new Event('input', { bubbles: true })); | |
| editTitleBtn.click(); | |
| // 去掉所有广告文件的选择 | |
| const treeNodes = await waitFor(() => layer2.querySelectorAll('.el-tree-node')); | |
| treeNodes.forEach(node => { | |
| const nodeText = node.textContent.trim(); | |
| if (isAds(nodeText)) { | |
| const checkbox = node.querySelector('.el-checkbox input[type="checkbox"]'); | |
| checkbox?.click(); | |
| } | |
| }); | |
| inProcess = false; | |
| } | |
| function findOverlay(title) { | |
| const overlays = Array.from(document.querySelectorAll('.el-overlay')); | |
| return overlays.find(overlay => overlay.style.display !== 'none' && overlay.textContent.includes(title)); | |
| } | |
| /** | |
| * 认定是广告的策略:名字前是【,或者名字中包含“更多“”下载”) | |
| */ | |
| function isAds(fileName) { | |
| return fileName.startsWith('【') || (fileName.includes('更多') || fileName.includes('下载')) | |
| } | |
| async function wait(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| async function waitFor(method, interval = 50) { | |
| return new Promise((resolve) => { | |
| const check = () => { | |
| const result = method(); | |
| if (result !== undefined && result !== null) { | |
| resolve(result); | |
| } else { | |
| setTimeout(check, interval); | |
| } | |
| }; | |
| check(); | |
| }); | |
| } | |
| async function waitAndClick(button, maxWait = 60000, checkInterval = 1000) { | |
| const startTime = Date.now(); | |
| while (Date.now() - startTime < maxWait) { | |
| if (!button.disabled) { | |
| button.click(); | |
| return; | |
| } | |
| await new Promise(resolve => setTimeout(resolve, checkInterval)); | |
| } | |
| console.log("等待超时,按钮仍然不可点击"); | |
| } | |
| // 等待页面加载完成 | |
| window.addEventListener('load', function() { | |
| // 每隔1秒执行一次检查 | |
| setInterval(detect, 1000); | |
| console.log('已启动自动检查程序,每隔1秒检查一次'); | |
| }); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment