// ==UserScript==
// @name qB_WebUI_标签批量替换tracker
// @name:en qB_WebUI_replace-trackers-with-tag
// @namespace localhost
// @version 1.0.1
// @author Kesa
// @description 利用 qBitorrent WebUI 的 tag API 批量替换 Tracker
// @description:en replace torrents tracker with tag in qBitorrent WebUI
// @license MIT
// @null ----------------------------
// @run-at document-end
// @match http://127.0.0.1:8080/
// @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js
// @downloadURL none
// ==/UserScript==
/**
* 参考: https://greasyfork.org/zh-CN/scripts/391688-水水-qbittorrent-管理脚本-qq-群-189574683
* 感谢这位大佬, 本脚本是参考了一下这个大佬的脚本进行简化而来的
* ----------------
* 代码流程:
* 1. 在 UI 界面开个 button 调出页面
* 2. 检测所有指定 tag 的种子 hash
* 3. 将所有指定 hash 的种子的 tracker 改为 指定的 tracker
*/
// NOTE: 0. 顶层配置
const host = window.location.href;
const baseURL = 'api/v2/torrents/';
const prevURL = host + baseURL;
/**fetch GET 封装
* @param {*} route 路由
* @returns Promise
*/
async function getFetch(route) {
try {
const response = await fetch(prevURL + route);
if (!response.ok) { throw new Error('请求失败'); }
const data = await response.json();
return data;
} catch (error) {
console.error(error);
return null;
}
}
/**fetch POST 封装
* @param {*} route 路由
* @param {*} data POST 内容
* @returns Promise
*/
async function postFetch(route, body = {}) {
try {
const response = await fetch(prevURL + route, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ data: body }),
data: body
})
if (!response.ok) { throw new Error('请求失败'); }
const data = await response.json();
return data;
}
catch (error) {
console.error(error);
return null;
}
}
function postXHR(route, body) {
// 创建一个新的XHR对象
var xhr = new XMLHttpRequest();
// 设置请求方法和URL
xhr.open('POST', prevURL + route, true);
// 设置请求头(可选)
xhr.setRequestHeader('Content-Type', 'application/json');
// 设置响应类型(可选)
xhr.responseType = 'json';
// 发送请求
xhr.send(JSON.stringify({ data: 'example' }));
// 输出发送的数据(可选)
console.log('发送的数据:', { data: 'example' });
// 处理响应事件
xhr.onload = function () {
if (xhr.status === 200) {
// 请求成功
console.log('请求成功');
console.log(xhr.response);
} else {
// 请求失败
console.log('请求失败');
}
};
// 处理错误事件(可选)
xhr.onerror = function () {
console.log('请求发生错误');
};
}
// NOTE: 1. 在 UI 界面开个 button 调出页面
const jq = jQuery;
const $ = jQuery;
// 添加菜单栏按钮
jq("#desktopNavbar > ul").append(
"
→批量替换 Tracker←",
);
// js-modal 绑定点击事件
jq(".js-modal").click(async function () {
// 请求 tag 列表
const tagList = await getFetch('tags')
console.log(tagList);
// 没有 tag 就提示新建
if (tagList.length == 0) { alert('请新建一个标签, 否则无法使用!'); return }
// 构建编辑框 html
let select = ``;
const strHtml = (select) => {
return `
标签:${select}
预览:
新 Tracker:
`};
// 用 qB_WebUI 自带的 MochaUI 调消息框出来
new MochaUI.Window({
id: "js-modal",
title: "批量替换 Tracker",
content: '',
scrollbars: true,
resizable: true,
maximizable: false,
closable: true,
paddingVertical: 0,
paddingHorizontal: 0,
width: 800,
height: 230,
draggable: true,
});
jq("#js-modal_content").append(strHtml(select));
jq("#js-modal_contentWrapper").css({
height: "auto",
});
// NOTE: 2. 检测所有指定 tag 的种子 hash
let selectedTag;
let torrentList;
let selectedList;
const _dom = document.getElementById('js-select');
_dom.onchange = async () => {
// 改变标签
selectedTag = _dom.value;
console.log('选择的标签: ', selectedTag)
// 获取改变标签的种子列表
torrentList = await getFetch(`info`)
// console.log(torrentList)
selectedList = torrentList.filter(el => el.tags.includes(selectedTag))
console.log('所选标签种子: ', selectedList);
// 显示所需种子列表个数
jq('#torrent-count').text(`数量: ${selectedList.length} `)
// 显示在 select 里
let html = ``;
for (const seed of selectedList) {
html += ``;
}
jq('#torrent-select').html(html)
}
// NOTE: 3. 将所有指定 hash 的种子的 tracker 改为 指定的 tracker
jq('button.js-replace').click(function () {
// 检查所选标签是否有种子数据
if (selectedList == undefined || selectedList.length == 0) { alert('所选标签没有种子数据!'); return }
// 检查新 tracker 是否存在
const input = jq('input.js-input').val()
if (input == "") { alert('目标 Tracker 没写!'); return }
// 检查 tracker 是否符合为正常 tracker 链接
const regex = /^(udp|http(s)?):\/\//;
const isReg = regex.test(input)
// console.log(isReg);
if (!isReg) { alert('目标 Tracker 不是有效的 Tracker 链接!'); return }
// 执行替换 Tracker 链接
try {
selectedList.map(async item => {
console.log(item.hash, item.tracker, input);
let res
// 有 tracker 就替换
if (item.tracker)
res = await getFetch(`editTracker?hash=${item.hash}&origUrl=${item.tracker}&newUrl=${input}`)
// 没有 tracker 就添加
else
res = await getFetch(`addTrackers?hash=${item.hash}&urls=${input}`)
console.log(res);
})
alert('操作可能成功了捏~多等几秒看看Tracker有没有变化捏~')
} catch (error) {
console.error(error)
alert('操作可能失败了捏~')
}
})
});