您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
通过 WebUI 的 API 批量替换 Tracker
当前为
// ==UserScript== // @name 「水水」qBittorrent 管理脚本「QQ 群:189574683」 // @namespace http://沉冰浮水.tk/ // @version 0.4 // @author 沉冰浮水 // @description 通过 WebUI 的 API 批量替换 Tracker // @null ---------------------------- // @contributionURL https://github.com/wdssmq#%E4%BA%8C%E7%BB%B4%E7%A0%81 // @contributionAmount 5.93 // @null ---------------------------- // @link https://github.com/wdssmq/userscript // @link https://afdian.net/@wdssmq // @link https://greasyfork.org/zh-CN/users/6865-wdssmq // @null ---------------------------- // @match http://127.0.0.1:8080/ // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js // @grant GM_xmlhttpRequest // jshint esversion:6 // ==/UserScript== /* global MochaUI */ (function () { "use strict"; "esversion: 6"; /* jshint multistr:true */ console.log("请备份%USERPROFILE%\\AppData\\Local\\qBittorrent"); const hostUrl = location.href; const $ = window.$; // return; // 构建编辑入口 $("#desktopNavbar>ul").append( "<li><a class=\"js-modal\"><b>→批量替换Tracker←</b></a></li>", ); let strHtml = "<div style=\"padding:13px 23px;\">\ <h2>分类:(必须指定分类,区分大小写)<h2><input class=\"js-input\" type=\"text\" name=\"category\" style=\"width: 97%;\"><br>\ <h2>旧 Trakcer:<h2><input class=\"js-input\" type=\"text\" name=\"origUrl\" style=\"width: 97%;\"><br>\ <h2>新 Tracker:<h2><input class=\"js-input\" type=\"text\" name=\"newUrl\" style=\"width: 97%;\"><br>\ <hr>\ <button class=\"js-replace\">替换</button>\ <hr>\ 「<a target=\"_blank\" title=\"爱发电 - @wdssmq\" href=\"https://afdian.net/@wdssmq\" rel=\"nofollow\">爱发电 - @wdssmq</a>」\ 「<a target=\"_blank\" title=\"QQ群 - 我的咸鱼心\" href=\"https://jq.qq.com/?_wv=1027&k=SRYaRV6T\" rel=\"nofollow\">QQ群 - 我的咸鱼心</a>」\ </div>"; // 点击事件 $("a.js-modal").click(function () { new MochaUI.Window({ id: "js-modal", title: "批量替换 Tracker", loadMethod: "iframe", contentURL: "", scrollbars: true, resizable: false, maximizable: false, closable: true, paddingVertical: 0, paddingHorizontal: 0, width: 500, height: 250, }); $("#js-modal_content").append(strHtml); }); $(document).on("click", ".js-replace", function () { // alert($(".js-input[name=category]").val()); let obj = { category: $(".js-input[name=category]").val().trim(), origUrl: $(".js-input[name=origUrl]").val().trim(), newUrl: $(".js-input[name=newUrl]").val().trim(), }; let err = 0; Object.keys(obj).map(function (key) { if (obj[key].trim() === "") { err = `${key}不能为空`; } }); if (err) { alert(err); return; } fnHttpGet(api.info, { category: obj.category }, function (data) { fnEdtList(data, obj.origUrl, obj.newUrl); }); return; }); // API List const api = { info: hostUrl + "api/v2/torrents/info", editTracker: hostUrl + "api/v2/torrents/editTracker", }; // let strTest = "udp://tracker.publicbt.com:80/announce"; // fnHttpGet(api.info, { category: "test" }, function(data) { // fnEdtList(data, strTest, "http://tracker.publicbt.com:80/announce"); // }); function fnEdtList(arrTorrents, origUrl, newUrl) { arrTorrents.map(function (item) { console.log(item.hash); fnEdtTracker(item.hash, origUrl, newUrl); }); // 计数略麻烦 // alert(`替换完成${arrTorrents.length}个`); } function fnEdtTracker(hash, origUrl, newUrl) { fnHttpGet(api.editTracker, { hash, origUrl, newUrl }); } // 基础函数 function $n(e) { return document.querySelector(e); } function $na(e) { return document.querySelectorAll(e); } function fnHttpGet( url, objPar = {}, callback = function (data) { console.log(data); }, ) { let strPar = Object.keys(objPar).length === 0 ? "" : "?" + Object.keys(objPar) .map(function (key) { return ( encodeURIComponent(key) + "=" + encodeURIComponent(objPar[key]) ); }) .join("&"); // console.log(url); GM_xmlhttpRequest({ method: "GET", // data: JSON.stringify(data), url: url + strPar, headers: { "User-agent": window.navigator.userAgent, "Content-Type": "application/x-www-form-urlencoded", }, onload: function (responseDetail) { if (responseDetail.status === 200) { callback(JSON.parse(responseDetail.responseText)); } else { console.log(responseDetail.status, responseDetail); } }, }); } })();