您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
qBittorrent Torrents Auto Delete
// ==UserScript== // @name qB Torrents Auto Delete // @namespace - // @version 1.0 // @description qBittorrent Torrents Auto Delete // @author Dreamray // @match http*://*/api/v2/* // @grant none // ==/UserScript== (function() { 'use strict'; /** * 思路非常简单:只考虑种子活动时间内的平均上传速度,达不到设定值且当前上传速度也达不到设定值,就删,不考虑什么黑车、慢车,只看上传速度,适合家宽使用 * 只删除上次活动时间距现在最久的种子:磁盘空间不足时只根据这一条件删种,其它条件太复杂也用不着,这一条就够了 * 有辅种处理逻辑:删除做种状态的种子时检测有无同名或同大小种子,有则只删除种子不删除文件,无则文件也删 * 只支持qb:抱歉本人只用qb,只在qb 4.4.3.1版本中测试过 * 默认打开testMode,运行一段时间,确定不会出现误删种子后再关闭 */ //设置部分: const testMode = 1, //测试模式用暂停代替删种,防止误删。测试无误后可修改值为0关闭 cycle = 2, //删种周期,也是向qb发起检查请求的周期,单位分钟,建议1分钟以上,太短了浏览嚣可能卡死 averageUpSpeedScale = 350, //平均上传速度标尺,单位KB,小于此值,且当前下载速度小于设定值,且是正在下载的种子,会被删 timeActiveScale = 5, //最小活动时间标尺,单位分钟,种子活动时间高于此值才计算活动时间内的平均上传速度 upSpeedScale = 320, //上传速度标尺,单位KB,本次检查的上传速度小于此值,且活动时间内的平均上传速度小于设定值,会被删 stalledDLTimeScale = 20, //种子未开始下载或下载中断后的等待时间标尺,超过此值的种子会被删,单位分钟 queuedDLTimeScale = 10, //种子由于队列设置未开始下载的等待时间标尺,超过此值的种子会被删,单位分钟(刷流工具有时会一次添加多个种子,而由于qb的最大活动下载数设置导致有排队下载的情况适用) minFreeSpace = 5, //最小磁盘剩余空间,单位GB,小于此数值时会删除上次活动时间距现在最久的种子,建设设置为删种周期内最大下载数据量的2倍或更多倍 // hrTrackerHour = { //有HR站点的 tracker地址域名部分:HR时长 // 'pt.btschool.club':20, // '52pt.site':24, // 'tracker.torrentleech.org':240, // 'tracker.tleechreload.org':240 // }, hrTrackerHourRatio = { //有HR站点的 tracker地址域名部分:{HR时长,分享率} 'pt.btschool.club':{hour:20,ratio:1}, //HR时长 和 分享率 通常为或关系,即一个达到要求即可 'www.nicept.net':{hour:120,ratio:2}, '52pt.site':{hour:24,ratio:-1}, //-1表示无分享率选项(无视分享率,只考察HR时长) 'tracker.torrentleech.org':{hour:240,ratio:-1}, 'tracker.tleechreload.org':{hour:240,ratio:-1} }, shuaPathList = [ //只删除以下目录内的种子(不包括子目录,如果种子存放在以下目录的子目录中,子目录也必须加到此列表),建议刷流种子统一放此目录中 'E:\\PT-Shua' ], keepCategoryList = [ //不删除以下分类的种子 'Movie', 'TV', 'H', 'Music', 'Exam' ] // , // freeTimeTrackerHour = { //免费时间短的站点tracker:最短免费时间,防止限免到期了还没下完,限免到期前删除(一般情况下用不着,像hh,8小时肯定出种了,不用担心下不完,暂时不实现这个功能了) // 'hhanclub.top':7 // } ; let nowDate, maindata, torrents, torrentsSorted, timeNow, // hrTrackers = Object.keys(hrTrackerHour), // hrHours = Object.values(hrTrackerHour), hrTrackers = Object.keys(hrTrackerHourRatio), hrHourRatio = Object.values(hrTrackerHourRatio), // freeTimeTrackers = Object.keys(freeTimeTrackerHour), // freeTimeHours = Object.values(freeTimeTrackerHour), method, delete_files, diskFreeSpace, torrentsSortedNames, torrentsSortedSizes, willDelTorrentsSum, deletedTorrentIndex, progress1Sum, resumeSuccessSum, countOccurrences = (arr, val) => arr.reduce((a, v) => (v === val ? a + 1 : a), 0), //setTimeout setInterval task = setInterval(check, cycle * 60000) ; // function isFreeTimeTracker(tracker){ // let domain = tracker.split('/'); // domain = domain[2]; // if(freeTimeTrackers.indexOf(domain) !== -1){ // return true; // }else{ // return false // } // } // function isFreeTimeHourExpireSoon(tracker,addedOn){ // let domain = tracker.split('/'); // domain = domain[2]; // let domainIndex = freeTimeTrackers.indexOf(domain); // timeNow = Math.floor(Date.now() / 1000); // if(timeNow - addedOn > freeTimeHours[domainIndex] * 3600){ // return true; // }else{ // return false; // } // } function isHrTracker(tracker){ let domain = tracker.split('/'); domain = domain[2]; if(hrTrackers.indexOf(domain) !== -1){ return true; }else{ return false } }; // function isHrHourFinished(tracker,seedingTime){ // let domain = tracker.split('/'); // domain = domain[2]; // let domainIndex = hrTrackers.indexOf(domain); // if(seedingTime > hrHours[domainIndex] * 3600 + 600){ //比站点规定的HR时间多10分钟,不确定这10分钟有没有必要 // return true; // }else{ // return false; // } // }; function isHrFinished(tracker,seedingTime,torrentRatio){ let domain = tracker.split('/'); domain = domain[2]; let domainIndex = hrTrackers.indexOf(domain); if(hrHourRatio[domainIndex].ratio > 0){ if( seedingTime > hrHourRatio[domainIndex].hour * 3600 + 600 || //做种时间达标了(保险起见比站点规定的HR时间多10分钟,不确定这10分钟有没有必要) torrentRatio > hrHourRatio[domainIndex].ratio //或者分享率达标了 ){ return true; }else{ return false; } }else{ if( seedingTime > hrHourRatio[domainIndex].hour * 3600 + 600 //做种时间达标了(保险起见比站点规定的HR时间多10分钟,不确定这10分钟有没有必要) ){ return true; }else{ return false; } } }; function isShuaFolder(savePath){ if(shuaPathList.indexOf(savePath) !== -1){ return true; }else{ return false; } }; function isKeepCategory(category){ if(keepCategoryList.indexOf(category) !== -1){ return true; }else{ return false; } }; function deleteTorrent(hash,name,reason,deleteFiles,callback){ method = testMode ? 'pause' : 'delete'; delete_files = testMode ? '' : '&deleteFiles=' + deleteFiles; let xhrDelete = new XMLHttpRequest(); xhrDelete.open('GET', '/api/v2/torrents/' + method + '?hashes=' + hash + delete_files, true); xhrDelete.send(); xhrDelete.onreadystatechange = function () { if (xhrDelete.readyState == 4 && ((xhrDelete.status >= 200 && xhrDelete.status < 300) || xhrDelete.status == 304)) { deletedTorrentIndex += 1; nowDate = new Date(); console.log(nowDate.toLocaleTimeString() + ':成功删除第 ' + deletedTorrentIndex + ' 个 -> ' + name + ' 原因:' + reason); callback(); } } }; function check(){ willDelTorrentsSum = 0; deletedTorrentIndex = 0; timeNow = Math.floor(Date.now() / 1000); //现在时间,unix格式 let xhrMaindata = new XMLHttpRequest(); xhrMaindata.open('GET', '/api/v2/sync/maindata', true); xhrMaindata.send(); xhrMaindata.onreadystatechange = function () { if (xhrMaindata.readyState == 4 && ((xhrMaindata.status >= 200 && xhrMaindata.status < 300) || xhrMaindata.status == 304)) { maindata = JSON.parse(xhrMaindata.responseText); diskFreeSpace = maindata.server_state.free_space_on_disk; // console.log('磁盘剩余空间:'); // console.log(diskFreeSpace / 1024 / 1024 / 1024 + 'G'); let xhrTorrents = new XMLHttpRequest(); xhrTorrents.open('GET', '/api/v2/torrents/info', true); xhrTorrents.send(); xhrTorrents.onreadystatechange = function () { if (xhrTorrents.readyState == 4 && ((xhrTorrents.status >= 200 && xhrTorrents.status < 300) || xhrTorrents.status == 304)) { torrents = JSON.parse(xhrTorrents.responseText); // console.log('原始种子列表:'); // console.log(torrents); torrentsSorted = torrents.sort((obj1, obj2) => (obj1.last_activity > obj2.last_activity) ? 1 : (obj1.last_activity < obj2.last_activity) ? -1 : 0); // console.log('排序后的种子列表:'); // console.log(torrentsSorted); torrentsSortedNames = torrentsSorted.map(item =>{ return item.name }); // console.log('排序后的种子列表摘出names:'); // console.log(torrentsSortedNames); torrentsSortedSizes = torrentsSorted.map(item =>{ return item.size }); // console.log('排序后的种子列表摘出size:'); // console.log(torrentsSortedSizes); for(let i=0;i<torrentsSorted.length;i++){ if( isShuaFolder(torrentsSorted[i].save_path) && //路径是刷流专放路径 !isKeepCategory(torrentsSorted[i].category) //非 保留的分类 ){ if( torrentsSorted[i].state == 'downloading' && //正在下载的种子 torrentsSorted[i].time_active > timeActiveScale * 60 && //活动时间大于设定值 torrentsSorted[i].uploaded / torrentsSorted[i].time_active < averageUpSpeedScale * 1024 && //活动时间内平均速度小于设定值 torrentsSorted[i].upspeed < upSpeedScale * 1024 //并且本次检查时的上传速度小于设定值 ){ if( torrentsSorted[i].progress < 0.5 //进度小于50%,无需考虑hr ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'平均速度小于设定值',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查'); }); break; }; if( torrentsSorted[i].progress > 0.5 && //进度大于50%,需要考虑hr !isHrTracker(torrentsSorted[i].tracker) //非 有hr ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'平均速度小于设定值',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查'); }); break; } }; if( torrentsSorted[i].state == 'stalledDL' && //未开始下载或下载中断的种子 timeNow - torrentsSorted[i].added_on > stalledDLTimeScale * 60 //等待下载时间超过设定值 ){ if( torrentsSorted[i].progress < 0.5 //进度小于50%,无需考虑hr ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'等待下载时间大于设定值',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查'); }); break; }; if( torrentsSorted[i].progress > 0.5 && //进度大于50%,需要考虑hr !isHrTracker(torrentsSorted[i].tracker) //非 有hr ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'等待下载时间大于设定值',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查'); }); break; } }; if( torrentsSorted[i].state == 'queuedDL' && //排队下载的种子 timeNow - torrentsSorted[i].added_on > queuedDLTimeScale * 60 //排队等待下载时间超过设定值 ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'队列下载时间大于设定值',true,function(){ //删除文件 console.log('种子已删除,继续处理其它种子'); }) }; if( diskFreeSpace < minFreeSpace * 1024 * 1024 * 1024 && //磁盘空间小于设定值 torrentsSorted[i].state == 'stalledUP' //做种但没在上传的种子 ){ if( !isHrTracker(torrentsSorted[i].tracker) //非 有hr ){ if( (countOccurrences(torrentsSortedNames,torrentsSorted[i].name) > 1 || countOccurrences(torrentsSortedSizes,torrentsSorted[i].size) > 1) //有辅种 ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'磁盘空间不足,删除上次活动时间距现在最久的种子(有辅种,保留文件)',false,function(){ //不删除文件 console.log('文件未删除,磁盘空间仍不足,继续删种'); }) }; if( (countOccurrences(torrentsSortedNames,torrentsSorted[i].name) <= 1 && countOccurrences(torrentsSortedSizes,torrentsSorted[i].size) <= 1) //无辅种 ){ willDelTorrentsSum += 1; setTimeout(function(){ deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'磁盘空间不足,删除上次活动时间距现在最久的种子和文件',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查磁盘剩余空间'); }); },1000) break; } }; if( isHrTracker(torrentsSorted[i].tracker) && //有hr (torrentsSorted[i].downloaded == 0 || isHrFinished(torrentsSorted[i].tracker,torrentsSorted[i].seeding_time,torrentsSorted[i].ratio)) //下载数据量为0不考虑hr或hr达标了 ){ if( (countOccurrences(torrentsSortedNames,torrentsSorted[i].name) > 1 || countOccurrences(torrentsSortedSizes,torrentsSorted[i].size) > 1) //有辅种 ){ willDelTorrentsSum += 1; deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'磁盘空间不足,删除上次活动时间距现在最久的种子(有辅种,保留文件)',false,function(){ //不删除文件 console.log('文件未删除,磁盘空间仍不足,继续删种'); }) }; if( (countOccurrences(torrentsSortedNames,torrentsSorted[i].name) <= 1 && countOccurrences(torrentsSortedSizes,torrentsSorted[i].size) <= 1) //无辅种 ){ willDelTorrentsSum += 1; setTimeout(function(){ deleteTorrent(torrentsSorted[i].hash,torrentsSorted[i].name,'磁盘空间不足,删除上次活动时间距现在最久的种子和文件',true,function(){ //删除文件 console.log('文件已删除,停止继续删种,等待下一次检查磁盘剩余空间'); }); },1000) break; } } } } }; if(willDelTorrentsSum >0){ nowDate = new Date(); console.log(nowDate.toLocaleTimeString() + ':本次检查共有 ' + willDelTorrentsSum + ' 个符合删除条件的种子'); } } } } } } })();