Docker 部署 Vertex 进行刷流
其实我已经不刷了,把方案和配置留存记录一下。一些脚本在上一篇文章里。
首先我建议你需要买个盒子,大陆家宽刷流的省省还有付费买商人盒子之类的,一点意义没有。
具体可见:PT盒子教程&如何种子竞速刷流
项目介绍
Vertex 是一个专为 PT(私有种子网络)玩家设计的追剧刷流一体化综合管理工具。它集成了多种功能,帮助用户高效地管理和追踪剧集,特别适合喜欢使用 PT 的用户群体。项目基于 JavaScript、Vue.js 和 Less 开发,使用 Node.js 作为后端技术栈,并支持 Docker 容器化部署,简化了环境配置和依赖管理。
主要功能
-
追剧管理:帮助用户追踪和管理剧集进度。
-
刷流支持:优化 PT 网络中的刷流任务,提升效率。
-
自动化任务:通过 Webhook 实现任务触发和通知。
-
媒体库集成:支持与 Emby 等媒体库工具集成,方便播放和管理
部署
其实官网已经有完善的部署
apt update -y &&apt upgrade -y &&apt install curl -y &&curl -fsSL https://get.docker.com -o get-docker.sh &&sh get-docker.sh &&timedatectl set-timezone Asia/Shanghai &&mkdir -p /root/vertex &&chmod 777 /root/vertex &&docker run -d --name vertex --restart unless-stopped --network host -v /root/vertex:/vertex -e TZ=Asia/Shanghai lswl/vertex:stable

访问Vertex
访问vertex存储路径/root/vertex/data/ 鼠标双击password查看初始密码。
cat /root/vertex/data/password

如果和上面一眼,默认vertex服务监听的3000端口
访问 ip:3000
默认用户admin,密码是上面获取的

配置
我这里就不在赘述一些qb的连接配置,TG的通知什么的。
Vertex的基础安装参照:https://wiki.vertex.icu(感谢栗佬)
下面部分配置来自互联网备份。有的原作者已经删帖,无法引用。
还有部分规则来自 Nodeseek-vertex刷流工具的规则
根据自身盒子io情况,硬盘大小和刷的站点,勾选删种和修改里面的参数,不要直接套用。
黑车1
(maindata, torrent) => { const categoryList = ["keep"]; let ruleData = [ { down: 10, up: 2 }, { down: 5, up: 1 }, ]; const { state, category, uploadSpeed, downloadSpeed } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } for (const rule of ruleData) { if ( state == "downloading" && downloadSpeed >= util.calSize(rule.down, "MiB") && uploadSpeed <= util.calSize(rule.up, "MiB") ) { return true; } }
return false;};
黑车2
(maindata, torrent) => { const categoryList = ["keep"]; const { state, category, uploadSpeed, downloadSpeed } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( state == "downloading" && downloadSpeed >= util.calSize(10, "MiB") && downloadSpeed / uploadSpeed >= 1.8 ) { return true; }
return false;};
第一版的黑车是通过写死的key和value然后for循环去取里面的数值,简单粗暴,但是在2.5g管以及10g管的情况下,判断就用不上了,维护成本过高。第二版的黑车主要思路是,下载速度除上传速度当大于1.8这区间的时候进行删种。分成写两个的原因是黑车1是针对刚开始发车追车时候判断的,避免一开始下载10上传5这种被误删。
无效做种
(maindata, torrent) => { const categoryList = ["keep"]; const stateList = ["uploading", "stalledUP"]; const { state, uploadSpeed, category, completedTime } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( stateList.indexOf(state) !== -1 && uploadSpeed <= util.calSize(512, "KiB") && moment().unix() - completedTime >= 5400 ) { return true; }
return false;};
无效做种是针对做种超过一个半小时的时候,并且上传小于512kb的种子。如果下载或者做种的种子本身过多,可以进行修改。
分享率
(maindata, torrent) => { const categoryList = [ "keep", "chdbits", "ourbits", "hdhome", "lemonhd", "pter", "audiences", "hdchina", "hdsky", "hddolby", ]; const { uploaded, size, category } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if (uploaded / size >= 3) { return true; }
return false;};
分享率就是3倍跳车,具体要改什么站点,就按格式改categoryList里的参数即可,每个人根据站点以及vip情况自行修改。
最长下载时间
(maindata, torrent) => { const categoryList = ["keep"]; const stateList = ["downloading", "stalledDL"]; const { state, addedTime, category, uploadSpeed } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( stateList.indexOf(state) !== -1 && moment().unix() - addedTime >= 57600 && uploadSpeed <= util.calSize(5, "MiB") ) { return true; }
return false;};
针对那些16个小时内没有下完的龟速种子,大部分情况下都是发种人盒子崩了什么的。上传速度如果大于5m的,不删除。
慢车 持续40s
(maindata, torrent) => { const categoryList = ["keep"]; const stateList = ["downloading", "stalledDL"]; const { state, uploadSpeed, progress, category, leecher } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( (moment().hour() >= 0 && moment().hour() <= 8) || maindata.leechingCount <= 10 || leecher >= 100 ) { return false; } if ( stateList.indexOf(state) !== -1 && uploadSpeed <= util.calSize(250, "KiB") && progress >= 0.1 ) { return true; }
return false;};
1.在0点-8点这个时间段,种子数量小于10个时跳过,下载人数大于100时跳过,保证夜间不误删,能有足够的种子。2.进度大于10%,上传速度小于250kb持续40的跳车。
长时间未开始
(maindata, torrent) => { const categoryList = ["keep"]; const { state, category, progress, addedTime } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( moment().hour() >= 0 && moment().hour() <= 8 && state == "stalledDL" && progress <= 0.05 && moment().unix() - addedTime >= 18000 ) { return true; } if ( state == "stalledDL" && progress <= 0.05 && moment().unix() - addedTime >= 14400 ) { return true; }
return false;};
删除0-8点这个时间段,超过五小时并且进度小于5%的种子。删除常规时间段,超过四小时并且进度小于5%的种子
下载人数少
(maindata, torrent) => { const categoryList = ["keep"]; const stateList = ["downloading", "stalledDL"]; const { state, category, leecher, uploadSpeed, addedTime } = torrent; if (categoryList.indexOf(category) !== -1) { return false; } if ( stateList.indexOf(state) !== -1 && leecher <= 10 && uploadSpeed <= util.calSize(500, "KiB") && moment().unix() - addedTime >= 900 ) { return true; }
return false;};
删除下载人数小于10,上传速度低于500kb,添加超过20分钟的非潜力种。
空1小时跳车
持续时间10-15s
(maindata, torrent) => { const categoryList = ["keep"]; const { state, category, progress } = torrent; const { eta } = torrent.originProp; if (categoryList.indexOf(category) !== -1) { return false; } if (state == "downloading" && category == 'hdsky' && progress >= 0.05 && progress <= 0.2 && (eta / 60) <= 70) { return true; }
return false;};
瓷器非免跳车
qb的分类必须和下方的判断一致。默认使用hdchina
(maindata, torrent) => { const categoryList = ["keep"]; const { state, category, addedTime } = torrent; const { num_incomplete } = torrent.originProp; if (categoryList.indexOf(category) !== -1) { return false; } if (state == 'downloading' && category == 'hdchina' && moment().unix() - addedTime >= 180 && num_incomplete <= 12) { return true; }
return false;};
岛非免跳车
qb的分类必须和下方的判断一致。默认使用chdbits
(maindata, torrent) => { const categoryList = ["keep"]; const { state, category, addedTime } = torrent; const { num_incomplete } = torrent.originProp; if (categoryList.indexOf(category) !== -1) { return false; } if (state == 'downloading' && category == 'chdbits' && moment().unix() - addedTime >= 180 && num_incomplete <= 40) { return true; }
return false;};
剩余空间删种
我这里是10g,你需要根据你的盒子自己改

根据种子的流行程度动态删除种子
超过七天才可能删除符合规则的种子,删种按分享率删除,每在3的基础上多0.1分享率就多留一天。
(maindata, torrent) => {const config = { deleteHours: 7 * 24 *3600, // 超时才删种的时间 ratioThreshold: 3, // 基础分享率一阈值(上传量 / 种子大小) enableLogging: false, // 是否启用日志记录}; const moment = require('moment'); // 确保 moment 模块可用 const now = moment().unix();
// 辅助函数:向 /vertex/log22/log.txt 中追加日志信息function logFailure(message) { if (!config.enableLogging) return; // 如果未启用日志记录,直接返回 const fs = require('fs'); const path = require('path'); const logFilePath = '/vertex/log22/log.txt'; const logDir = path.dirname(logFilePath); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } const logMessage = `${new Date().toISOString()} - ${message}\n`; fs.appendFileSync(logFilePath, logMessage);}
// 计算下载时间和分享率 const downloadTime = now - torrent.addedTime; const downloadTime2 = downloadTime - config.deleteHours; const extraDays = Math.max(0, Math.floor(downloadTime2 / 86400)); // 每天86400秒 // 动态调整分享率上限 const ratioLimit = config.ratioThreshold + extraDays * 0.1;
// 检查种子是否完成下载 if (now - torrent.completedTime < config.deleteHours ) { return false; // 不删除种子 } if (torrent.completedTime > 0 && torrent.ratio < ratioLimit && torrent.uploadSpeed < 10* 1024) { return true; // 删除种子 } return false; // 不删除种子};
适用于Hz Cloud 馒头盒子
效果:适应馒头标盒机制,对于小流量的机器不当冤大头额外上传不计入上传流量的部分。同时又不暂停,可以获取魔力。规则:不实际删除种子,超过分享率后设置限速100k,两天后解除限速(适用于馒头盒子机制:新种标盒,两天盒子规则消失,盒子规则限制时间内只计算3倍种子大小上传量,标盒过期后取消限制)
// 参数:// maindata:qBittorrent 的全局数据// torrent:当前种子的信息(maindata, torrent) => { // 统一配置对象(所有时间单位均为小时)const config = { pauseThresholdHours: 72, // 当种子添加时间小于72小时(3天)且分享率大于阈值时触发暂停 resumeThresholdHours: 72, // 当种子添加时间大于72小时时用于恢复(这里与暂停阈值相同) ratioThreshold: 3, // 分享率一阈值(上传量 / 种子大小 > 3时触发操作) enableLogging: false, // 是否启用日志记录 downloaderId: 'e88e88dd', // 下载器ID,指定适用的下载器ID speedLimit: 100 * 1024, // 限速值(单位:KB/s)};
// 辅助函数:向 /vertex/log22/log.txt 中追加日志信息function logFailure(message) { if (!config.enableLogging) return; // 如果未启用日志记录,直接返回 const fs = require('fs'); const path = require('path'); const logFilePath = '/vertex/log22/log.txt'; const logDir = path.dirname(logFilePath); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } const logMessage = `${new Date().toISOString()} - ${message}\n`; fs.appendFileSync(logFilePath, logMessage);}
// 获取下载器ID和下载器对象 const downloaderId = config.downloaderId; const client = global.runningClient[downloaderId];if (!client) { logFailure(`未找到下载器 ${downloaderId}`); return false;}
const moment = require('moment'); // 确保 moment 模块可用 const now = moment().unix(); // 计算种子添加后的持续时间(单位:秒) const ageSeconds = now - torrent.addedTime; // 根据配置中的小时数转换为秒数 const thresholdSeconds = config.pauseThresholdHours * 3600; const thresholdSeconds2 = config.resumeThresholdHours * 3600; // 计算“分享率一”:上传量 / 种子大小 const shareRatio1 = torrent.size > 0 ? torrent.uploaded / torrent.size : 0;
// 如果种子添加时间小于阈值(72小时)且分享率大于阈值,则限速种子 if (ageSeconds < thresholdSeconds && shareRatio1 > config.ratioThreshold) { client.setSpeedLimit(torrent.hash, 'upload', config.speedLimit) .then(() => { logFailure(`限速种子成功: ${torrent.name}`); }) .catch(err => { logFailure(`限速种子 ${torrent.name} 失败: ${err}`); }); }
// 如果种子当前已限速且添加时间大于阈值,则取消限速 if (ageSeconds > thresholdSeconds2) { client.setSpeedLimit(torrent.hash, 'upload', 0) // 设置为0表示取消限速 .then(() => { logFailure(`取消限速成功: ${torrent.name}`); }) .catch(err => { logFailure(`取消限速 ${torrent.name} 失败: ${err}`); }); }
// 始终返回 false,不会执行删种操作
return false;};
RSS规则
可以根据更多规则选择要刷的种子,对于小盘鸡友好,完全不会超过设置的保种体积,同时计算体积带矫正,和性能缓解,2分钟前算过体积就不重新计算了,不会每个种子都算一遍全盘种子大小。可以限制下载数量,种子大小,文件名等,可以自定义开启关闭功能进行组合。15G小盘鸡也能刷了,添加种子前计算下载器中种子的大小,这个不是vertex自带的剩余空间大小,那个实际因为没下完种子可能会超过空间导致占满空间,当然可用提前分配缓解,但是还是不符合需求,这个可以直接实现完全不会超过设置空间。可以说是非常人性化了。注意要设置相应下载器的id。
(torrent) => { // 配置部分:这些部分可以自定义 const config = { downloaderId: 'e88e88dd', // 下载器ID,指定适用的下载器ID enableLogging: false, // 是否启用日志记录 enableSeedingVolumeLimit: true, // 是否启用保种体积限制 seedingVolumeLimit: 50 * 1024 * 1024 * 1024, // 保种体积限制(单位:字节),例如14GB enableTaskCountLimit: true, // 是否启用下载器任务数量限制 taskCountLimit: 200, // 下载器任务数量限制,设置最大任务数 enableSizeLimit: true, // 是否启用种子大小限制 sizeLimitRange: [1 * 1024 * 1024 * 1024, 10 * 1024 * 1024 * 1024], // 种子大小范围(单位:字节) enableFileNameMatchLimit: false, // 是否启用文件名匹配限制 fileNameRegex: /sample|test/i // 文件名正则匹配,支持正则表达式 };
const fs = require('fs'); const path = require('path');
// 获取本地时间字符串 function getLocalTimeString() { return new Date().toLocaleString('zh-CN', { hour12: false }); }
// 辅助函数:向 /vertex/log22/log.txt 中追加日志信息 function logFailure(message) { if (!config.enableLogging) return; // 如果未启用日志记录,直接返回 const logFilePath = '/vertex/log22/log.txt'; const logDir = path.dirname(logFilePath); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } const now = getLocalTimeString(); fs.appendFileSync(logFilePath, `[${now}] ${message}\n`); }
// 获取下载器ID和下载器对象 const downloaderId = config.downloaderId; // 当前下载器ID const client = global.runningClient[downloaderId]; if (!client) { logFailure(`未找到下载器 ${downloaderId}`); return false; }
// 2. 保种体积限制:先检查 /vertex/${downloaderId}/size.txt 中存储的时间和累计体积 if (config.enableSeedingVolumeLimit) { const sizeFilePath = `/vertex/${downloaderId}/size.txt`; const sizeDir = path.dirname(sizeFilePath); if (!fs.existsSync(sizeDir)) { fs.mkdirSync(sizeDir, { recursive: true }); } let currentCumulative = 0; // 当前累计体积(字节) let fileTimestamp = 0; // 文件中记录的时间(秒) const now = Math.floor(Date.now() / 1000); // 当前时间,单位:秒
// 尝试读取文件,如果存在则解析内容(格式:timestamp,cumulativeSize) if (fs.existsSync(sizeFilePath)) { try { const data = fs.readFileSync(sizeFilePath, 'utf8').trim(); if (data) { const parts = data.split(','); if (parts.length === 2) { fileTimestamp = Number(parts[0]); currentCumulative = Number(parts[1]); } } } catch (err) { logFailure(`读取 size.txt 失败: ${err}`); } } else { // 如果文件不存在,则计算累计体积并创建文件 client.maindata.torrents.forEach(t => { currentCumulative += Number(t.size || 0); }); fileTimestamp = now; try { fs.writeFileSync(sizeFilePath, `${fileTimestamp},${currentCumulative}`); } catch (err) { logFailure(`写入 size.txt 失败: ${err}`); } }
// 如果下载器中没有任务,则重置累计体积为 0 if (!client.maindata.torrents || client.maindata.torrents.length === 0) { currentCumulative = 0; fileTimestamp = now; try { fs.writeFileSync(sizeFilePath, `${fileTimestamp},${currentCumulative}`); } catch (err) { logFailure(`重置 size.txt 失败: ${err}`); } } // 如果文件中的时间超过2分钟,则重新计算累计体积 else if (now - fileTimestamp > 120) { currentCumulative = 0; client.maindata.torrents.forEach(t => { currentCumulative += Number(t.size || 0); }); fileTimestamp = now; try { fs.writeFileSync(sizeFilePath, `${fileTimestamp},${currentCumulative}`); } catch (err) { logFailure(`更新 size.txt 失败: ${err}`); } }
// 确保 torrent.size 为数值 const torrentSize = Number(torrent.size);
// 将累计体积加上当前种子的大小,与保种体积限制比较 if ((currentCumulative + torrentSize) >= config.seedingVolumeLimit) { logFailure(`保种体积限制不通过:累计体积 ${currentCumulative} + 当前种子 ${torrentSize} 超过限制 ${config.seedingVolumeLimit}`); return false; } }
// 3. 下载器任务数量限制:检查下载器当前任务数量 if (config.enableTaskCountLimit) { if (client.maindata.torrents.length >= config.taskCountLimit) { logFailure(`任务数量限制不通过:当前任务数量 ${client.maindata.torrents.length} 超过限制 ${config.taskCountLimit}`); return false; } }
// 4. 种子大小限制:检查种子的大小是否在规定范围内 if (config.enableSizeLimit) { const size = Number(torrent.size); if (size < config.sizeLimitRange[0] || size > config.sizeLimitRange[1]) { logFailure(`种子大小限制不通过:种子大小 ${size} 不在范围 ${config.sizeLimitRange[0]} - ${config.sizeLimitRange[1]}`); return false; } }
// 5. 文件名匹配限制:检查文件名是否符合正则匹配 if (config.enableFileNameMatchLimit) { const fileName = torrent.name; if (!fileName.match(config.fileNameRegex)) { logFailure(`文件名匹配限制不通过:种子名称 "${fileName}" 未匹配正则 ${config.fileNameRegex}`); return false; } }
// 如果所有条件都满足,则表示添加该种子。 // 在返回 true 前,如果启用了保种体积限制,则更新 /vertex/${downloaderId}/size.txt if (config.enableSeedingVolumeLimit) { const sizeFilePath = `/vertex/${downloaderId}/size.txt`; let cumulative = 0; let fileTimestamp = 0; if (fs.existsSync(sizeFilePath)) { try { const data = fs.readFileSync(sizeFilePath, 'utf8').trim(); if (data) { const parts = data.split(','); if (parts.length === 2) { fileTimestamp = Number(parts[0]); cumulative = Number(parts[1]); } } } catch (err) { logFailure(`读取 size.txt 更新部分失败: ${err}`); } } cumulative += Number(torrent.size); try { fs.writeFileSync(sizeFilePath, `${fileTimestamp},${cumulative}`); } catch (err) { logFailure(`更新 size.txt 失败: ${err}`); } }
return true;};
Hetzner Cloud 自动检测流量并删除开机脚本
实时监测Hetzner流量状况,超过预设值时自动删除并用制定快照创建新机器。
属于滥用,我就不公开了,想写的自己写吧。
