Skip to content

Instantly share code, notes, and snippets.

@helloint
Last active October 1, 2025 14:35
Show Gist options
  • Select an option

  • Save helloint/f1e26da41683e6b5a11d7f490d4e425e to your computer and use it in GitHub Desktop.

Select an option

Save helloint/f1e26da41683e6b5a11d7f490d4e425e to your computer and use it in GitHub Desktop.
NAS迅雷新建任务自动化
// ==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