OSR 上手指南:用一套系统把 PT 下载、网盘同步、STRM 生成和刮削重命名串起来
本文基于 OSR 仓库代码与官方 Wiki 整理,覆盖从 Docker 部署到 PT 订阅全链路自动化的完整流程。
2026-09 更新:补上 Plex 接入、订阅一键诊断、字幕体检、过滤规则历史回放、观看状态同步、电影系列/人物订阅、TG 订阅指令、通知快捷操作、MCP 服务端与 AI 功能、STRM 增量扫描、配置备份与恢复、历史日志检索。
项目地址:github.com/907739769/OSR | Wiki:项目 Wiki
一、OSR 是什么
OSR 的全称是 OpenList STRM Relay,是一套围绕 OpenList(AList 分支) 构建的影视 STRM 管理系统。
如果你的观影链路是这样的:
PT 站点 → qBittorrent 下载到本地 → 上传/同步到网盘 → 生成 STRM → Emby/Jellyfin 播放
那么中间那几步「同步」「生成 STRM」「按 TMDb 规范重命名」「下完通知我一声」,基本都是靠脚本、cron 和人肉巡检拼起来的。OSR 做的事情就是把这条链路做成一个有界面、有记录、有通知、可重试的系统:
- STRM 生成:递归扫描网盘目录,为视频文件生成对应的
.strm流媒体文件; - 文件夹同步:本地 ↔ 网盘、网盘 ↔ 网盘的增量/全量同步,带复制任务监控与重启兜底;
- TMDb 刮削与重命名:按元数据识别电影/剧集,模板化重命名,并生成 NFO、海报等刮削产物;
- 一致性检查:双向孤儿扫描,找出「记录在库里、文件没了」和「文件在库里、记录没了」的残骸;
- PT 订阅:接入 Torznab 索引器与下载器,RSS 自动追剧、缺集补搜、洗版、H&R 追踪、自动删种、转移做种;
- 追剧日历与缺集体检:按播出日期铺开每一集,并把「播出多日仍未入库」的集连同「为什么还缺」一起列出来;单条订阅还能一键诊断,逐集看最近一轮搜索卡在哪条规则上;
- 第三方回调:开放 API 接收 qBittorrent 等下载完成通知,自动触发同步 → 复制 → STRM 全流程;
- 多端通知与控制:Telegram Bot、企业微信自建应用、通用 Webhook、Bark、Gotify 五条通知渠道,由一张路由表决定「哪种通知发到哪些渠道、发给谁」,前两者还能反向下指令、在通知上直接点「重试」「补搜」;
- MCP 与 AI:内置 MCP 服务端,Claude Desktop、Cursor 这类本地 AI 助理凭令牌就能查订阅、看追剧进度、触发任务;另有自然语言建过滤规则、种子标题 AI 兜底解析和每周周报;
- 配置备份与恢复:各项配置连同 PT 订阅导出成一个 JSON,换机器或重装时逐项预览后合并回来。
技术栈上是 Java 25(Spring Boot 4.0.6,开了 Preview 特性用虚拟线程)+ Vue 3 + Vuetify 3 + MyBatis-Plus + MySQL 8.0,前端带 PWA,移动端可以直接「添加到主屏幕」当 App 用。
二、安装部署
2.1 前置要求
| 项 | 要求 |
|---|---|
| Docker | 20.10+ |
| Docker Compose | v2 |
| 内存 | 至少 2GB 可用 |
| 磁盘 | 按 STRM/日志/上传量准备,建议 10GB 起 |
| 依赖服务 | 一个可访问的 OpenList/AList 实例(必需) |
2.2 准备部署目录
最简单的方式是直接克隆仓库:
git clone https://github.com/907739769/OSR.git
如果只想部署、不想要源码,也可以只下载 docker-compose.yml、.env.example、nginx.conf 三个文件到一个自建目录里。
2.3 配置环境变量
复制 .env.example 为 .env,逐项改掉里面的占位值:
cp .env.example .env
# MySQL 配置
MYSQL_ROOT_PASSWORD=你的强密码
MYSQL_DATABASE=osr
# 应用专用数据库账号(非 root,仅拥有该库权限,MySQL 镜像会自动创建)
DB_USERNAME=osr_app
DB_PASSWORD=你的强密码
# JWT 密钥,至少 32 字符,建议 64 位随机串
JWT_SECRET=请替换为强随机字符串
# PT 下载器密码 / 索引器 apikey 的加密密钥,至少 32 字符
PT_CREDENTIAL_SECRET=请替换为强随机字符串
# 时区
TZ=Asia/Shanghai
# 前后端分域部署时才需要,生产环境由 Nginx 同源代理 /api,通常留空
# CORS_ALLOWED_ORIGINS=http://your-domain:80
几个容易踩的点:
MYSQL_ROOT_PASSWORD和DB_PASSWORD是两个独立的东西,后端连库用的是DB_USERNAME/DB_PASSWORD这个最小权限账号,不复用 root;PT_CREDENTIAL_SECRET不配也能跑,但会退化成内置兜底密钥并打警告日志——它只能保证下载器密码、索引器 apikey 不明文落库,不具备真正的机密性,生产环境务必显式配置;JWT_SECRET改了之后所有人都要重新登录,这是预期行为。
生成随机串可以直接用:
openssl rand -base64 48
2.4 使用预构建镜像(推荐)
仓库里的 docker-compose.yml 默认是本地构建(build:),需要先 mvn package 才能用。日常部署建议改成官方镜像,把 backend、frontend 两个服务的 build: 段换成 image::
backend:
image: jacksaoding/osr-backend:latest
# build:
# context: .
# dockerfile: Dockerfile.backend
frontend:
image: jacksaoding/osr-frontend:latest
# build:
# context: .
# dockerfile: Dockerfile.frontend
2.5 启动
docker compose pull
docker compose up -d
首次启动时 MySQL 要初始化,后端会等健康检查通过后再连库并自动执行建表/迁移脚本,耐心等一两分钟。
2.6 验证
docker compose ps
docker compose logs -f backend
docker compose ps 里三个容器都是 running、restarts 为 0 才算正常。backend 还带健康检查(/api/health,匿名接口,只探数据库),STATUS 要显示 (healthy)。
前端的 depends_on 用的是 condition: service_healthy,所以后端起不来时前端整个不会启动。 这是有意的——比「Nginx 起来了却一路 502」更容易定位。首次启动要跑完全部迁移脚本,healthcheck 的 start_period 给了 180 秒,这段时间显示 starting 属于正常。
想单独确认后端就绪:
curl http://你的IP:6895/api/health
排查启动失败的关键:Java 异常写在容器内的 /data/logs/sys-error.log,不在 docker stdout(stdout 只有启动 banner)。容器反复重启时,先让它崩溃后停住再看日志:
docker update --restart=no osr-backend && docker restart osr-backend
然后把日志捞出来看:
docker cp osr-backend:/data/logs ./tmp
2.7 端口与数据卷
| 端口 | 服务 | 用途 |
|---|---|---|
| 80 | frontend (Nginx) | Web 界面 +/api 反代 + WebSocket 代理 |
| 6895 | backend | 后端 API |
| 3306 | mysql | 数据库 |
| 卷 | 说明 |
|---|---|
/data |
宿主机挂载,存放 upload/、logs/、strm/ |
mysql-data |
数据库数据 |
端口冲突就改 docker-compose.yml 里的 ports 映射,比如把 "80:80" 改成 "8080:80"。
2.8 首次登录
浏览器打开 http://你的IP:80:
- 用户名:
admin - 密码:
openliststrm666
登录后第一件事:点右上角头像改掉默认密码。
三、基础配置
3.1 配置 OpenList 连接
进入 系统管理 → 参数管理,这里是 OSR 所有全局开关的所在地。必填的只有两项:
| 参数键 | 名称 | 说明 |
|---|---|---|
openlist.server.url |
OpenList 访问地址 | 如 http://192.168.1.10:5244 |
openlist.server.token |
OpenList API token | 在 OpenList 后台「其他」页面获取 |
强烈建议顺手配上的:
| 参数键 | 名称 | 说明 |
|---|---|---|
openlist.api.apikey |
OSR 接口 Apikey | 第三方回调(qB 等)的鉴权凭证,不配则回调接口不可用 |
openlist.tmdb.apikey |
TMDb API Key | 重命名/刮削/PT 订阅都依赖它 |
可选的 AI 兜底(文件名正则和 TMDb 都识别不出来时才调用):
| 参数键 | 默认值 | 说明 |
|---|---|---|
openlist.openai.endpoint |
官方地址 | 支持任意 OpenAI 兼容端点 |
openlist.openai.apikey |
空 | |
openlist.openai.model |
gpt-5-mini |
注意:Telegram Bot 相关参数改完需要重启后端才生效;TMDb / OpenAI 参数在每次重命名时实时读取,改完立即生效。
3.2 值得了解的性能与行为开关
这几个参数默认值就很合理,但了解它们能帮你在大库场景下调优:
| 参数键 | 默认 | 作用 |
|---|---|---|
openlist.strm.outputdir |
/data/strm |
STRM 文件生成根目录 |
openlist.strm.encode |
0 |
STRM 内路径是否 URL 编码 |
openlist.strm.downloadsub |
0 |
生成 STRM 时是否顺带下载字幕 |
openlist.copy.minfilesize |
10 |
复制的最小文件大小(MB),过滤掉样片、说明文件 |
openlist.copy.strm |
1 |
复制完成后是否自动生成 STRM |
openlist.api.refresh |
1 |
源目录同步列举时强制刷新网盘,保证增量正确性 |
openlist.api.traversal.refresh |
0 |
目标目录遍历时是否强制刷新。默认走缓存,对网络盘快非常多 |
openlist.api.traversal.concurrency |
10 |
目录遍历并发度,范围 1–64。大目录树调高能显著提速 |
openlist.copy.monitor.maxminutes |
600 |
复制任务监控最长时长,超时标记异常,避免无限期挂着 |
openlist.local.allowedroots |
/data |
本地目录浏览接口的根目录白名单,防止管理端枚举整个宿主机文件系统 |
openlist.tmdb.metadata.language |
zh-CN |
TMDb 元数据语言 |
openlist.tmdb.image.language |
zh |
TMDb 图片语言偏好 |
openlist.tmdb.image.size |
original |
图片尺寸,可改 w780/w500 省带宽和存储 |
登录安全相关(登录失败同时按「账号」和「来源 IP」两个桶计数,任一超阈值即临时锁定):
| 参数键 | 默认 | 作用 |
|---|---|---|
sys.login.maxRetryCount |
5 |
账号桶阈值,0 = 关闭 |
sys.login.ipMaxRetryCount |
30 |
IP 桶阈值,用来拦换着用户名喷的扫描器。刻意放宽,避免同一出口 IP 下的正常用户被连坐;0 = 关闭 |
sys.login.lockMinutes |
10 |
锁定时长,同时也是失败次数的统计窗口;0 = 关闭全部登录锁定 |
参数设置页按配置键前缀分组(
openlist.strm.*、openlist.notify.*、sys.login.*…)。加同前缀的配置会自动落进对应分组,全新前缀落进「其他」。
3.3 配置路径映射
进入 OpenListStrm → 路径管理,把「本地路径」和「网盘路径」建立对应关系。核心要求是两侧的目录树结构必须一致,OSR 才能把一侧的相对路径原样映射到另一侧。
举个例子:
本地:/download/pt/电影/华语电影/功夫/功夫.mp4
网盘:/115网盘/影视/pt/电影/华语电影/功夫/功夫.mp4
└────┬────┘ └──────────┬──────────────┘
映射前缀差异 结构必须完全相同
这一步配错,后面同步任务和回调自动化都会「跑成功但文件放错地方」,值得多花两分钟核对。
四、核心功能怎么用
4.1 同步任务
OpenListStrm → 同步任务配置 → 新建:
- 填任务名;
- 选源路径与目标路径(网盘 ↔ 网盘、网盘 ↔ 本地都支持);
- 设置任务启用状态;
- 保存后可以手动执行,也可以交给定时任务。
执行结果在 同步任务记录 页查看。这个页面支持:
- 单条 / 批量重新处理失败记录,顶部还有一个「重试全部失败」(和 TG 的
/retry走同一套逻辑); - 单个 / 多个网盘文件删除;
- 顶部状态统计条,点一下就按状态筛选;每条记录显示文件大小与失败原因,PC 端点开详情抽屉能看到完整路径和 OpenList 任务 ID。
批量操作超过 20 条会转后台执行,页面会如实提示并在几秒后自动刷新;有处理中的记录时页面每 10 秒静默刷新一次,全部结束后停。
4.2 STRM 任务
OpenListStrm → STRM 任务配置 → 新建:
- 填任务名;
- 配置扫描路径、输出目录、文件过滤规则;
- 需要时展开「本任务覆盖」,单独设置输出根目录、是否下载字幕、最小文件体积——只有填了的项才覆盖,其余继续用参数管理里的全局值;
- 保存后手动执行或等定时任务。
结果在 STRM 生成记录 页查看,同样支持单条/批量重新处理与网盘文件删除,视频和字幕分开显示、可按类型筛选。
增量扫描:网盘目录大了以后,每天全量列一遍目录是 STRM 任务最慢的部分。在任务弹窗里打开「增量扫描」后,定时执行会跳过「修改时间没变、生成设置没变、上次里面的文件都处理完了」的叶子目录。几点要知道:
- 只跳叶子目录(只有文件、没有子目录的那层),上层目录每轮照常列——目录的修改时间不会向上传递,按整棵子树跳的话会漏掉已有季里新增的一集;
- 每隔
openlist.strm.fullscan.days(默认 7 天)自动全量扫一次兜底,顺带清理已不存在的目录快照; - 只有定时执行走增量,页面手动执行、复制完成自动触发、TG 按路径生成仍是全量,想强制刷一遍直接手动点就行。
任务级覆盖是按输出路径反查任务生效的,而不是靠调用方传任务进来:无论这次生成是页面点的、定时任务跑的、复制完成自动触发的,还是 Telegram 里 /strm <路径> 手打的,只要路径落在某个任务的目录下就走该任务的覆盖值。否则同一个目录会因为「谁触发的」而输出到两个不同的根目录、长出两棵 STRM 树,一致性检查还会把其中一棵报成孤儿。
URL 编码开关和视频/字幕扩展名刻意不可按任务覆盖:前者有三个解码侧消费者要从
.strm内容反解回网盘路径,后者是全站字典,分库配置只会制造解不开的历史数据。
4.3 重命名任务
前置条件:参数管理里配好 openlist.tmdb.apikey;想要 AI 兜底的话再配上 OpenAI 三项。
OpenListStrm → 重命名任务配置 → 新建,指定源目录和目标目录,保存后执行。
识别链路是:本地正则抽取 → TMDb 增强 → AI 补充(仅在前两步都识别不出时)→ 模板渲染。识别结果、原文件名到目标文件名的映射,都能在重命名明细页逐条查看——新文件名下方会列出识别到的类型、季集、分辨率、编码、来源、发布组和 TMDb 链接,识别错了一眼就能看出错在哪一项。
分类规则和命名模板在重命名规则设置里改:模板预览同时给电影和剧集两份样例,「测试」页会给出目标目录,还能直接用编辑中还没保存的模板来试;规则表会标出被前面更宽的规则完全覆盖、永远命不中的规则——最常见的是一条什么条件都没填的规则被挪到了中间,它后面的全部失效。
有几个设计上的取舍值得知道:
- 目标库里的主文件是
Files.copy出来的副本,源文件(下载器的保种目录、网盘挂载点)OSR 一律不动——所以系统里根本没有「删除源文件」的入口,这是刻意的:删了它等于毁保种。 - 「删产物」和「删记录」是两件不同的事,界面上也是分开的。只删数据库记录会导致三个后果:孤儿扫描再也发现不了那批文件、刮削共享元数据(
tvshow.nfo等)的兄弟计数算错、手动重跑任务时会把源文件当成没处理过再复制一份出来。前端确认框里会把这几条写清楚。
4.4 定时任务
系统监控 → 定时任务 里内置了三个作业:
| 任务 | 默认时间 |
|---|---|
| 重命名任务 | 每天 02:00 |
| 同步任务 | 每天 03:00 |
| STRM 任务 | 每天 05:00 |
可以改 Cron 表达式、手动触发、停用。但不要改调用目标字符串。
另有一个默认暂停的「每周周报」(每周一 09:00):汇总最近 7 天的同步 / STRM / 重命名 / PT 下载情况,配了 OpenAI 时附一段点评,走通知路由里的「每周周报」类型发出。想用的话启用它,或先点「执行一次」看看效果。
「执行一次」是后台受理的:点完立即返回,不会再因为复制、STRM 这类分钟级任务撞上前端超时——以前的表现是界面报错、任务其实还在跑,再点一次就真的并发了两遍。不允许并发的任务在上一轮没结束时不再受理。列表上能看到下次/上次执行时间,执行日志也有了真实的开始时间和耗时。
4.5 一致性检查(孤儿扫描)
OpenListStrm → 重命名一致性检查。它是双向扫描:
- 正向(记录 → 文件):数据库里有记录,目标位置的文件不见了;
- 反向(文件 → 记录):目标库里有文件,却找不到对应记录,细分为
local_extra(多余文件)、metadata_only(只有元数据没有视频,Emby 里会显示成一个空剧集)、empty_dir(空目录)。
反向扫描带 mtime 基线:早于对应任务创建时间的文件一律不上报。判据是「任务建起来之前就躺在库里、之后又没被动过的东西不是它产出的」——没这道闸,一个混着历史文件的媒体库每轮会报出成千上万条无关项。
发现的项可以清理或忽略,忽略过的项后续轮次直接跳过,不会反复打扰。
4.6 实时日志与看板
- 实时日志:WebSocket 推送,info / debug / error 三级,移动端自适应。可以暂停、按 traceId 过滤(点一下日志行里的 traceId 就只看这一条链路)、导出当前过滤结果;断线重连不再清屏或重复刷 500 行。
- 检索历史:实时日志页切到「检索历史」,在全部日志文件(包括滚动出去的分片)里按关键字 / 正则 / 级别 / 起止时间检索。命中堆栈里的类名会把首行一起带出来,按 traceId 检索能带出整段堆栈。单次扫描有 512MB / 15 秒上限,触发时会写明截断原因。
- 数据看板与首页待办:首页汇总各类任务的统计,另有一块「待办」——失败记录、下载器离线、媒体服务器连不上、失败下载待处理——点一下直达对应的记录。某一路没取到数时会提示「没取到」,而不是装作没有待办。
五、把 qBittorrent 接进来:下载完成自动上云 + 生成 STRM
这是 OSR 最实用的自动化之一:qB 下载完成 → 回调 OSR → 同步到网盘 → 生成 STRM,全程无人值守。
5.1 前置条件
- 参数管理里配好
openlist.api.apikey; - qBittorrent 所在网络能访问到 OSR 的 6895 端口(或走前端 80 端口的
/api反代); - 本地目录、网盘目录、qB 下载目录三者的目录结构对得上(见 3.3)。
5.2 接口说明
POST /api/openliststrm/notify/notifyByDir
Header: X-API-KEY: <openlist.api.apikey 的值>
Content-Type: application/json
请求体四个字段全部必填:
{
"srcDir": "/download/pt",
"srcDst": "/115网盘/影视/pt",
"qbDlRootPath": "/downloads",
"qbDlFilePath": "/downloads/电影/华语电影/功夫/功夫.mp4"
}
| 字段 | 含义 |
|---|---|
srcDir |
源目录(本地下载根目录在 OSR/OpenList 视角下的路径) |
srcDst |
目标目录(对应的网盘路径) |
qbDlRootPath |
qB 的下载根目录 |
qbDlFilePath |
本次下载完成的资源路径 |
OSR 拿到后会用 qbDlFilePath 去掉 qbDlRootPath 前缀得到相对路径,再判断是视频文件还是目录:视频走单文件同步,目录走批量同步。
5.3 qB 侧配置
在 qB 的 config 目录下建一个 notify.sh(按自己的路径改前三个变量):
#!/bin/sh
srcDir="/download/pt"
srcDst="/115网盘/影视/pt"
qbDlRootPath="/downloads"
apiKey="你在参数管理里配的 apikey"
osrUrl="http://osr-host:6895"
curl -s -X POST "${osrUrl}/api/openliststrm/notify/notifyByDir" \
-H "Content-Type: application/json" \
-H "X-API-KEY: ${apiKey}" \
-d "{\"srcDir\":\"${srcDir}\",\"srcDst\":\"${srcDst}\",\"qbDlRootPath\":\"${qbDlRootPath}\",\"qbDlFilePath\":\"$2\"}"
然后在 qB 的「下载完成时运行外部程序」里填:
sh /config/notify.sh "%G" "%F"
六、PT 订阅:把追剧也自动化
这是 OSR 里最复杂、也最有意思的模块。它做的事情是:你订阅一部剧,剩下的搜索、筛选、推送下载、进度追踪、入库确认、缺集补搜、洗版、H&R 保种提醒,全部自动完成。
6.1 前置条件
| 组件 | 说明 |
|---|---|
| Torznab 索引器 | Jackett 或 Prowlarr,至少配好一个 PT 站 |
| 下载器 | qBittorrent 或 Transmission,OSR 能访问到 |
| 媒体服务器 | Emby、Jellyfin 或 Plex,必需;可以同时配多台 |
| TMDb API Key | 参数管理里的 openlist.tmdb.apikey |
媒体服务器为什么是必需的:OSR 判断「这一集齐了」的依据不是下载完成,而是媒体服务器的库里真的能查到它。下完 ≠ 活干完——文件还要传上网盘、还要生成 STRM、Emby 还要扫到,中间任何一步断了这一集就不算数。
菜单在哪:PT 相关菜单按「我要干什么」分成四组,都直接挂在侧边栏根节点——PT 追剧(追剧日历 / 缺集体检(含字幕体检) / 订阅管理 / 热门自动订阅)、PT 下载(下载记录 / 统计仪表盘 / 转移做种)、PT 规则(过滤规则 / 洗版规则 / 黑名单)、PT 接入(索引器 / 下载器 / 媒体服务器)。分组标题已经带 PT,子菜单名不再重复写前缀。
6.2 配置索引器
PT 接入 → 索引器 → 新增:
- 索引器名称、Torznab URL(从 Jackett/Prowlarr 复制)、API Key;
- 分类代码(剧集
5000、电影2000); - 轮询周期,默认 600 秒。
轮询周期不要设太短。系统内部对同一索引器有最小 2 秒间隔的限流,撞到站点 429 之后会进入 300 秒冷却——冷却期内的请求会走「背压」分支快速失败,这类失败既不计入失败次数也不清零,专门避免「限流 → 退避放大 → 拉取窗口从几分钟变成几小时」的正反馈。
系统还会做 RSS 覆盖度校验:记录上一轮的最大发布时间作为游标,判断本轮的窗口下沿是否覆盖得上。覆盖不全会告警提醒你缩短周期。(这里刻意用「时间位置」而不是「上一轮首条种子还在不在」当游标——后者在有置顶种子、种子被删、guid 带一次性 token 的站上会恒定误报。)
6.3 配置下载器
PT 接入 → 下载器 → 新增:
- 类型(qBittorrent / Transmission)、地址、账号密码(支持 HTTPS);
- 保存路径必须落在某个已启用的同步任务的源目录内——这是硬要求,否则下载完的文件不会流进「同步到网盘 → 生成 STRM」这条管道;
- 标签,默认
osr-pt(OSR 靠它认领自己推的种子); - 最大并发下载数;
- 角色:
DOWNLOAD(订阅下载池)或SEED_ONLY(只做种的保种机)。
角色这个字段值得单独说:如果你加了一台「接 IYUU 转移+辅种、开着自动删种」的保种机,不把它标成 SEED_ONLY 的话,订阅会把新剧集也往它上面推——而那台机器的清理规则是按保种设计的(做满 N 小时就删),正在补的剧集下上去会被当成保种种子清掉。
媒体服务器(PT 接入 → 媒体服务器):类型选 Emby / Jellyfin / Plex,填地址和 API Key(Plex 填 X-Plex-Token)。「测试连接」会直接告诉你真实原因——401 是 Key 填错、连不上是地址/端口/网络、返回了 HTML 多半是反代把请求转给了别的服务;成功时回显服务器名和版本,局域网里跑着两台 Emby 时这一句能省不少排查。
启用的媒体服务器全部参与入库判定,任一台查到就算入库。所以「电影在 Emby、剧集在 Jellyfin」或者「新旧两台并存迁移中」都能直接用,配置页上会显示每台最近的连通状态。Emby/Jellyfin 的用户 ID 可以在表单里直接拉取,填上之后才能同步观看状态(见 6.11)。
6.4 过滤规则(可选但强烈建议)
PT 规则 → 过滤规则:
- 硬过滤:最小做种数、体积上下限(支持按集折算)、分辨率/片源/标签要求、标题关键词、描述关键词、是否必须带中文字幕、是否规避 H&R 站点;
- 排序维度:默认
RESOLUTION,FREE,SEEDERS,SIZE,可调整优先级。
「描述排除词」是标题关键词之外单独的一维,因为有一类属性标题里根本不写。蓝光原盘最典型:国内站只在种子描述里标一句「原盘」,标题与压制版逐字同构,两者都解析成 source=BluRay,来源白名单分不开;体积上限虽然能挡住原盘,却会连体积区间重叠的 REMUX 一起切掉,而 REMUX 是 mkv、播放器本来吃得下。
两条相反的兜底是刻意的:标题为空一律淘汰,描述为空一律放行。标题是索引器必给的字段,描述不是——不少索引器压根不返回
<description>,按「判不出即淘汰」处理会把这些站点的候选整批清光。
改规则之前有三个工具可以先试:
- 用一句话描述规则:比如「只要 1080p 以上的 WEB-DL,不要杜比视界,体积每集 5G 以内」,AI 生成一份草稿填进表单,逐字段校验,你确认后才保存(需要配 OpenAI);
- 规则试算:贴一个种子标题(可以附上体积、做种数),看它在编辑中、还没保存的规则下会不会被淘汰、因为哪一条;
- 历史回放:用最近 3 / 7 / 30 天真实搜到过的候选,比较「已保存的规则」和「编辑中的草稿」,列出会新通过和会新淘汰的种子、属于哪条订阅、因为什么——改完规则心里没底时最有用。回放只读,不发起任何搜索。
6.5 创建订阅
PT 追剧 → 订阅管理:
- 搜索剧名/片名(走 TMDb);
- 选中作品;剧集还要选季;
- 保存——保存那一刻会立即触发一次补充搜索,不用等下一轮 RSS。
订阅列表可以按「自动补搜开没开」「有没有已播出的缺集」筛选、按已播缺集数排序,也能批量开关自动补搜、批量立即补搜(串行执行、带进度、可以中途停)。手动搜索(卡片「更多 → 搜索补齐」)可以限定站点,候选列表能按关键词、站点、单集/区间包/整季筛选。
多用户场景下,订阅页和追剧日历顶部会出现归属筛选(我的 / 公共 / 其他用户),管理员视图会在别人的订阅卡片上标出归属人。
6.6 后台是怎么跑的
创建完订阅后,四个后台任务接管一切:
| 任务 | 周期 | 职责 |
|---|---|---|
| RSS 轮询 | 60 秒 | 拉索引器最新种子,匹配订阅 |
| 自动补搜 | 30 分钟 | 按订阅粒度主动搜缺失集 |
| 下载追踪 | 30 秒 | 跟进度、选文件、排除非目标集 |
| 媒体库同步 | 10 分钟 | 去 Emby/Jellyfin 确认这一集真的入库了 |
| 播出日期同步 | 12 小时 | 从 TMDb 拉每集播出日期,追剧日历的数据来源 |
| 缺集体检提醒 | 每日 | 把播出多日仍未入库的集聚合成一条提醒 |
| 自动删种 | 60 分钟 | 按体积区间 + 做种时长分级删种(默认关) |
| 转移做种 | 15 分钟 | 把做够时长的种子搬到另一台下载器(默认无规则) |
| 观看状态同步 | 1 小时 | 从媒体服务器读每集看没看过,供订阅卡片展示与补搜排序 |
| 保种快照 | 1 小时 | 记录各下载器的累计上传量与保种体积,供保种看板用 |
单集状态机:
MISSING(缺失,等待匹配)
↓ 推送到下载器
IN_FLIGHT(在途)
↓ 下载完成 + 同步上云 + 媒体库确认
IN_LIBRARY(已入库,完成)
↓ 触发洗版
UPGRADING(下载更高画质版本中)
任意环节失败 3 次以上 → BLOCKED(熔断,需人工在下载记录页重试)
几个实现细节挺能体现这个模块的成熟度:
- 多集包以暂停态推送:推送那一刻不知道包里有哪几集,先暂停,等下载器返回文件列表、选出目标集文件后再启动。单集/电影不暂停(没有选错的可能),磁力链也不暂停(暂停态下下载器不下元数据,会永远卡住)。
- 包内一个目标集都没有时会中止,而不是照常下发排除指令——把全部文件设成不下载会得到一个 0 字节、永远挂着的僵尸任务,白占并发名额。
- 只缺一两集时先试单集,季包降级成兜底。早期是季包无条件优先,结果「季包里恰好没有第 41 集、站上明明有单独的第 41 集,却永远只下季包」成了稳态。
- 标题里没集号时会去种子 description 里找。有一类日更剧发布组把同一季每一集都发成标题逐字相同的种子,集号只写在描述里,不解析的话会被反复当成整季包白推。
- OSR 默认从不删种,只有两个受控例外:从未下完也从未做种的废种,以及你逐个下载器显式开启的自动删种。
6.7 追剧日历与缺集体检
这两个页面回答的是两个不同的问题。
追剧日历(PT 追剧 → 追剧日历)按播出日期把已订阅剧集的每一集铺在格子里,一眼看出这周有哪几集、哪几集还是灰的。播出日期来自 TMDb,由后台任务每 12 小时同步(首轮兼做存量回填,升级上来的库不需要额外动作);查询本身是纯 SQL 范围查询,翻月不打 TMDb。两个刻意的取向:TMDb 撤掉日期时不清空已有值(撤档信息本身不可靠,清掉会让这一集从日历上凭空消失),并且只写日期确实变了的行(否则每 12 小时把整表刷一遍,看起来像天天都有变化)。
缺集体检(PT 追剧 → 缺集体检)回答的是「这一格为什么还是灰的」。它把播出超过 N 天(默认 3 天)仍未入库的集列出来,分成四档——分档不是严重程度,是处置方向:
| 分档 | 含义 | 该往哪儿查 |
|---|---|---|
| 逾期缺失 | 压根没下到 | 搜索链路:关键词、索引器、过滤规则 |
| 在途逾期 | 下了但没进库 | 下载/上传链路:同步任务、STRM 任务 |
| 已熔断 | 连续失败达阈值 | 需要人工介入,去下载记录页重置 |
| 无播出日期 | 连「逾期」都算不出来 | 未定档或 TMDb 未录入,通常只需等 |
每一集还带一条诊断,直接说清下一步:未开启自动补搜(可在本页一键开启)、补搜没搜到候选、候选被过滤规则淘汰、已熔断、已下好卡在上传或刮削(不需要重下)、正常在流程里。
这个页面的存在本身来自一个设计取舍:自动补搜默认是关的。每条开着的订阅每轮都要向每个索引器打满一整份检索计划,全量开启会让追完的老剧长期空转;代价是「订阅建完就没再管过、集一直缺着」这个最常见的场景在改造前一条提醒都没有——补搜落空通知只对开着开关的订阅发,追剧日历只按日期铺格子、不回答为什么。体检补的是这层可见性,而不是去改默认值。
几个实现上的取向:
- 诊断挂在集上,不挂在订阅上。同一条订阅完全可能一部分集缺着、另一部分卡在上传,压成一个「主诊断」就得定一套武断的优先级,而那个优先级对用户没有意义。
- 整个体检是一次纯 SQL 查询,不打任何外部请求,刷新页面不会变成一轮 TMDb/索引器调用。
- 电影不参与体检。它没有播出日期、逾期天数恒为空,而且「上映后几周内没有资源」是正常状态不是故障,报出来只会把真正缺集的剧集淹掉。这只影响体检的可见性,电影的 RSS 匹配、自动补搜、手动搜索一律照常。
- 通知只发「逾期缺失」一档(页面展示全部四档)。另外三档各自已经有人管:在途逾期由「入库卡住」负责、熔断在转 BLOCKED 那一刻通知过、无播出日期那档拿去打扰只会稀释信号。这条边界一旦模糊,同一集会从两三个地方各通知一次,用户很快会把整类通知关掉。
- 按收件人聚合成一条,不是每条订阅发一条——首次启用时积压的订阅可能有几十条,逐条发比不提醒还糟。去重靠指纹:缺的集变了立刻再发,没变则按
pt.health.notify-repeat-days(默认 7 天)重提醒。只按「发过就不再发」是不行的——一部永远补不上的剧提醒一次之后就再无声息,而它最该被记住。
阈值默认 3 天是刻意的宽松:热门剧几小时内就有资源,但冷门剧、原盘小组、等字幕的片子拖一两天是常态,判早了会把一批正在正常走流程的集报成问题,而一个总在误报的看板用户看两次就不看了。
日历里未播出的缺集显示为「待播出」,点开某一集可以直接「查看诊断」或「搜这一集」;体检页也支持带 ?subId= 只看一条订阅。
订阅一键诊断补的是体检覆盖不到的部分:刚播出一两天的集,以及电影。订阅卡片「更多 → 一键诊断」会先列订阅级的前提问题(没开自动补搜、没有可用索引器、订阅不在订阅中、有单独的过滤覆盖),再逐集列出最近一轮搜索拿到了几个候选、各因为什么原因被淘汰了多少个,弹窗里可以就地立即补搜。它只读日志、不发起搜索。TG 里 /diagnose 编号、企微里 诊断 编号 得到的是同一份结果。
字幕体检是缺集体检页里的一张卡片,点「检查」才加载:它去媒体服务器上查每一集实际的字幕流和音轨,分成中文字幕、中文音轨、语言未知、没有中字、未解析媒体信息几类,华语作品直接跳过。直接查媒体服务器而不是从种子标题猜,是因为标题只能说明「没写」,说明不了「没有」。目前支持 Emby / Jellyfin;硬字幕(烧在画面里的)查不到,页面上会注明。
6.8 集号有三套,OSR 会自动对齐
这是长篇动画(航海王一类)最容易踩空的一处建模:
- OSR 本地与 PT 种子用季内相对集号(
One Piece S23E13); - TMDb 主数据对这类剧用绝对集号(第 23 季 = 1156..1181);
- 媒体库按刮削结果组织,可能是上面任意一种(实测有用户的 Emby 把 1172 集全平铺在 Season 1、按绝对号编号)。
种子标题自己就印证了对应关系:One Piece S23E13 Episode 1168。OSR 把 TMDb 集号对齐后落库,日历取日期、对账取集号,因此三处都能正常工作:入库判定(本季本地集号 → 本季 TMDb 集号 → 整部剧任意季的 TMDb 集号,逐级兜底)、搜索与匹配(站上 S23E18 与 S01E1173 两种命名并存,两种都搜得到、都匹配得上)、包内文件识别(种子里的 One Piece S01E1174.mkv 会被归一化回本地集号,不会被误判成「这个包里没有目标集」而中止)。
这类 bug 的症状极其隐蔽:下载记录明明显示 COMPLETED,集状态却永远 MISSING,订阅进度一直卡着,日志里一切正常。
已知缺口:字幕组的
[Sakurato] One Piece - 1173 [2160p]这类裸数字命名仍然匹配不上,卡点在标题解析而不是集号。
6.9 自动补搜的节奏
自动补搜(订阅编辑页里的开关,默认关闭)每 30 分钟检查一次,每条订阅按自己的间隔(默认 24 小时)实际执行。这个节奏上有四层修正,每层都对应一个具体故障:
- 未播出的集不参与。 一部刚播到第 3 集的 12 集新番,剩下 9 集恒为缺失,不做这层的话每轮都打满一整轮索引器请求、每轮落空,而用户收到的是「未找到可用资源,可检查关键词与索引器配置」——真实原因是还没播。省下的请求是次要的,避免这条误导才是主因。
- 候选订阅在 SQL 层就收窄(进行中 + 开关开着 + 确实有缺失集)。追完的老剧长期留在进行中是常态,拉全部再内存过滤等于每轮为它们各查一次集表。
- 到期时刻带一点按订阅 id 派生的抖动(0 ~ +20%,只向后偏)。否则首次启动时所有订阅同时到期,串行跑完之后又几乎同时到期——这个抱团是自我维持的,不会自己散开。
- 单轮有总耗时预算(默认 20 分钟,刻意小于 30 分钟的心跳)。超预算的订阅原样留到下一轮,下轮天然从断点接着走,不会饿死。刻意不用并发解决:限流器是全局的,多条订阅同时搜只会在限流器上排队,撞上等待上限后静默跳过。
还有两条关于「落空」的:按连续落空次数退避(24 → 48 → 96 小时,封顶一周,命中一次立刻回到基准)——片源确实不存在的老剧原本会永远每 24 小时打满一整轮请求,而这件事从现象上根本看不出来;落空通知会去重,除首次外只有淘汰原因的种类变了才再发一次,「压根没搜到候选」与「候选全被 freeOnly 淘汰」处置方向完全相反,这个翻转恰恰是最需要知道的一次变化,不会被去重吃掉。
6.10 转移做种
PT 下载 → 转移做种。把 qBittorrent 上已经做够时长的种子搬到 Transmission(或另一台下载器)继续做种,腾出主下载器的任务槽。这是 IYUU「转移」功能的自建实现。
⚠️ 只覆盖「转移」,不覆盖「辅种」。 辅种要拿 infohash 去其它站点找同一份资源,依赖 IYUU 服务端的站点索引与各站 passkey,OSR 没有也造不出这些数据。装 IYUU 是为了辅种的话,这个功能替代不了它。
一条规则就是一对「源下载器 → 目标下载器」加筛选条件:最短做种时长、体积区间、包含/排除标签、路径映射(两边挂载一致时留空)、目标端标签、单轮上限、校验超时、校验通过后是否删源端种子。规则之间没有优先级,各管一对下载器、各自独立执行。默认每 15 分钟扫一轮,页面上也能「预览」和「立即执行」。
它是个跨轮次的状态机:本轮「导出 → 暂停态加种 → 触发校验」,下一轮「读校验结果 → 启动 → 删源端种子」。中间态落库而不是放内存——校验要跑几分钟到几十分钟,放内存里的话进程一重启,目标端就留下一批暂停态的孤儿种子。
几条硬性安全边界:
- 目标端一律以暂停态加入、校验到 100% 才启动。 直接以运行态加进去的话,保存路径一旦对不上(路径映射配错是本功能最常见的故障),下载器会把整个种子重新下载一遍。
- 撤销目标端种子时永远不删文件。 那份文件正是源下载器在做种的数据,删了等于让源端种子立刻变成「文件丢失」、在它所属的站点上记一次 H&R——这是整个功能里唯一能造成真实数据损失的一步。
- 顺序是「先启动目标端,再删源端」。 反过来的话中间那段窗口两边都不在做种,站点看到的是一个突然消失的种子。
- H&R 考核中的种子不转移。 换下载器后做种时长要从零重新累计,更要紧的是站点的 H&R 限额是下发到原下载器上的,限额不跟着种子搬家。同理,还有集停在在途/洗版中的记录也不转移:种子下完不等于活干完,文件还要传网盘。
- Transmission 不能作为转移来源——它的 RPC 里没有导出 .torrent 内容的接口(唯一沾边的字段给的是服务端本地路径)。前端的源下载器下拉直接把它滤掉。
- 源端的文件选择会跟着搬。 一个「只下了其中几集」的季包在 qB 里照样显示 100%(进度是相对已选文件算的),而导出的 .torrent 不含文件优先级,原样加到目标端就是全选、校验必然到不了 100%,被判成「该路径下没有这份数据」而回滚。这在本项目里根本不是边缘情况——OSR 自己就会给季包排除非目标集。
- 失败之后不会无限重试:6 小时冷却 + 累计 3 次后停止。失败不改变源端种子的状态,下一轮的判定条件与上一轮完全相同,没有闸门的话任何持续性故障都会变成「每轮转一次、每轮失败一次、每轮发一条通知」。配置改对之后,用记录弹窗里的「清除失败记录」解除——否则种子会因为历史失败次数永远不再被转移,比原来的刷屏更糟。
6.11 其它进阶功能
洗版(画质升级):在升级规则里开启,必须指定 cutoff 目标(分辨率/片源/标签至少填一项)——没有终止条件的话每一集都会永远搜下去把索引器配额烧干,所以三项全空时洗版不激活。判定维度只有分辨率、片源、标签、发布组四项,刻意不引入做种数/体积/免费状态:那些值随时间连续变化,会导致无限循环洗版。洗版不会自动删旧文件。
热门自动订阅:按榜单自动建订阅,可设最低评分、最低评分人数、排除类型、限定地区,单轮新增上限默认 5 部。数据源有四类:
- TMDb 榜单:每日/每周趋势、热门、正在播出等;
- RSSHub 豆瓣榜单:TMDb 的热门以欧美内容为主,华语和日韩的「热」在豆瓣上才看得出来。豆瓣条目没有 TMDb ID,OSR 按「归一化标题全等 + 年份差不超过 1 年」去 TMDb 补全——宁可漏也不订错,匹配不上的会记成「未匹配」写进执行日志,看得到、能手动补订;
- 电影系列:填一个 TMDb 合集的 ID 或页面链接,订阅系列里的全部电影,新出的续集下一轮自然补进来;
- 人物:填导演或演员的 TMDb ID / 链接,只订他担任导演、或演员表前 5 位、且近一年内上映或尚未上映的电影——要的是「他的新片出了告诉我」,不是把他全部作品都下下来。
你手动删掉的订阅,自动订阅不会再把它加回来。
种子黑名单:拉黑特定发布组或种子,后续不再匹配。
H&R 追踪:按索引器配置最短做种时长和分享率要求(或关系,达标任一即可)。H&R 是站点属性不是种子属性——Torznab 协议没有标准 H&R 字段,只能整站判定。OSR 只做告警,不会也无法自动补救。追踪是跨下载器的:种子被 IYUU 转移走了会去其它下载器的快照里找回来,而不是直接判成违规。
自动删种:按体积区间 + 做种时长分级删除(比如 >50GB 做满 3 天删、其余做满 7 天删)。五条护栏一条都没省:
- 总开关默认关,且要逐个下载器显式开启;
- 没有启用任何规则就一个都不删(空规则集 = 没有规则说该删它);
- H&R 考核中的记录一律保护;
- 还有集停在在途/洗版中的记录同样保护——种子下完不等于活干完,文件还要传网盘,大文件跨天是常态;
- 每轮有删除上限,超出的会记日志而不是静默截断。
删除以辅种组为最小单位(按内容路径分组),组内每个种子都达标才整组删,任一个不达标整组保留——删掉一个种子的文件会让共用这份文件的其它种子立刻变成「文件丢失」,那是在别的站上记 H&R。
观看状态同步:每小时从媒体服务器读一次每集看没看过(Emby/Jellyfin 需要在媒体服务器配置里填用户 ID,Plex 直接读),订阅卡片的进度后面会显示「已看 N 集」,悬停看最近观看时间。自动补搜每轮会先补最近在看的剧,单轮预算用完时被推到下一轮的是没人在看的老剧。多台服务器取并集,读不到的那台不参与,全都读不到时保留原值。
统计仪表盘:下载量趋势、索引器命中率、搜索淘汰原因分布、失败原因分布,统计卡、趋势点、失败扇形都可以点进去下钻到对应的下载记录。成功率按「完成 /(完成 + 没被接替的失败)」算——一集失败后又被别的种子补上了,就不该再拉低成功率。底部还有一块只对管理员可见的「保种与上传」:每日上传量、保种体积趋势、上传最多的种子与低效保种(做种满 7 天、分享率仍低于 0.2 的,最值得考虑腾地方),每日上传量按下载器累计计数器求差,删种不会让它变成负数。
下载记录:按订阅归属隔离——普通用户只看得到自己订阅的记录。可以按推送/完成/失败日期、失败原因、H&R 状态、索引器、下载器筛选;失败之后同一集已经有新推送的,会标「已由 #x 接替」、不再给重试。不打算处理的失败可以忽略,首页「失败下载待处理」就不再算它(统计里的失败数照算)。管理员可以按保留天数清理旧记录,只删终态、不在 H&R 考核中的。
6.12 PT 排障速查
| 现象 | 排查方向 |
|---|---|
| 一直不下载 | 先点订阅卡片的「一键诊断」,逐集看卡在哪条规则上;再看索引器状态和「搜索淘汰原因」看板 |
| 下载完了还显示在途 | 同步任务/STRM 任务有没有执行;媒体服务器配置页的连通状态;文件到底传上网盘没有 |
| 想放宽/收紧过滤规则又怕改坏 | 过滤规则页的「历史回放」,先看改完会多放过和多挡掉哪些种子 |
| 某集卡住不动 | 大概率已 BLOCKED,去下载记录页手动重试 |
| 反复失败 | 看「搜索淘汰原因」统计,通常是过滤规则卡太死 |
| 剧一直缺集,却收不到任何提醒 | 自动补搜默认关,落空通知只对开着开关的订阅发。去「缺集体检」页看诊断,可就地一键开启 |
| 长篇动画搜不到 / 下了却不算入库 | 集号有三套(见 6.8)。裸数字命名(- 1173)目前仍匹配不上 |
| 搜索结果比预期少一批 | 慢索引器跑不完整份计划被放弃了尾部步骤,日志里有 warn 说明是哪个索引器、放弃了几步 |
| 转移做种反复失败 | 多半是路径映射配错或源端是部分下载的季包。累计 3 次后会停止重试,改对配置后要「清除失败记录」解除 |
七、通知与远程控制
OSR 有五条通知渠道:Telegram Bot、企业微信自建应用、通用 Webhook、Bark、Gotify。各自在参数管理里配置,留空即不启用,可以同时开。
7.1 通知路由:发不发、发给谁
早期版本是每个渠道自己读一个 types 参数判断该不该发,渠道一多就没法统一配置,也没有「收件人」这一维。现在这件事收口到一张路由表(系统管理 → 通知路由):「发不发、发给谁」由路由表决定,渠道实现只管「怎么发」。
一行对应一组「通知类型 × 渠道」,可以分别设置是否发送与收件人范围,每个渠道都能发一条测试消息。通知类型有十种:
| 类型 | 说明 |
|---|---|
| 系统告警 | 索引器故障、复制超时等 |
| 订阅命中 | 匹配到候选并已推送下载器 |
| 下载完成 | |
| 下载失败 | 带失败原因;熔断提示拼在同一条里,不再单发 |
| 媒体库入库 | 对账确认某集真的进库了 |
| H&R 保种 | 达标可安全删种 / 达标前种子消失了 |
| 补搜落空 | 只对开着自动补搜的订阅发 |
| 入库卡住 | 文件已下好、迟迟没进媒体库 |
| 缺集逾期 | 有集播出多日仍未匹配到资源,由缺集体检发出 |
| 每周周报 | 由定时任务「每周周报」发出,默认暂停 |
通知带快捷操作:下载失败的通知附「重试 / 拉黑 / 进度」,补搜落空附「立即补搜 / 进度」,缺集逾期附每部剧的「补搜」(最多 4 个)。操作本质就是一条聊天指令:Telegram 上渲染成内联按钮,企业微信上是一行「可直接回复:补搜 12」的提示,其余渠道只收正文。点了之后按下载记录/订阅的归属校验权限,补搜和重试都在后台跑,完成后再补发一条结果。
三条容易做错的语义,都是刻意这么定的:
- 路由缺失按「发送」处理。 新增通知类型或新增渠道时路由行还没补上,宁可多发也不能静默丢——丢通知的故障用户根本发现不了。
- 「仅归属人」档在通知无归属时回退默认接收人,不是丢弃。 系统级告警(索引器故障、复制超时)本来就没有归属人,理解成丢弃会让这类告警凭空消失。
- 不支持分人的渠道一律退化为广播。 TG 只有一个 chat id、Webhook 只有一个 URL,配置页对这些渠道不展示收件人选项——给出一个不生效的开关比缺少这个功能更糟。
拆分已有通知类型时会配一条迁移,把新类型的路由行按拆分来源逐渠道复制一份。新增取值本身是安全的(缺失按「发送」处理),但拆分会让用户对旧类型的关闭设置落空——明确关掉「下载完成」的人升级后突然开始收到 H&R 提醒,他的设置没变、行为却变了。
还有两个文案上的约定值得一提:PT 通知的第一行统一是「哪部作品的哪一集」——命中、完成、失败、季包讲的本就是同一集,说法不一致时用户对不上号(原先命中说《三体》S01E05、完成和失败只给种子标题,而国内站的标题常带一长串站点前缀);以及片段缺失时整段不写,不写「未知」——一句「分辨率:未知」不帮用户做任何判断,只把真正有用的几段挤下去。
通知文案统一按 Telegram 的 HTML 模式编写,其余渠道会自动还原成纯文本(去标签 + 解 HTML 实体,顺序不能换),所以片名里的 & 不会在企微/Bark/Gotify 上显示成 &——而 & 在 PT 片名和组名里相当常见。
7.2 Telegram Bot
参数管理里配 openlist.tg.token 和 openlist.tg.userid,重启后端生效。支持的指令:
| 指令 | 作用 |
|---|---|
/strm |
执行 STRM 任务 |
/strmdir |
生成指定路径的 STRM |
/sync |
执行同步任务 |
/syncdir |
同步 OpenList 指定目录 |
/rename |
执行重命名任务 |
/retry |
重试所有失败任务 |
/checkorphan |
执行重命名一致性检查 |
PT 订阅指令与企业微信共用同一套逻辑(纯中文指令如「我的订阅」同样可用):
| 指令 | 作用 |
|---|---|
/sub 剧名 / /submovie 片名 |
搜索并订阅剧集 / 电影,结果以内联按钮选片、选季 |
/subs |
我的订阅 |
/downloading / /recent |
正在下载的集 / 最近入库的集 |
/progress 编号 |
订阅进度,进度页上有补搜、暂停/恢复、刷新按钮 |
/search 编号 |
立即补搜缺集,后台执行,完成后回报结果与真实的落空原因 |
/diagnose 编号 |
一键诊断:缺的集为什么还没下到 |
/pause 编号 / /resume 编号 |
暂停 / 恢复订阅 |
/pthelp |
订阅指令说明 |
选片、选季这类按钮带会话 id,点到上一轮搜索留下的旧按钮会提示已过期,不会订错片。
发送范围由通知路由决定(早期版本的 openlist.notify.tg.types 已被路由表取代)。TG 只有一个 chat id,因此它在路由里一律是广播。
7.3 企业微信自建应用
比 TG 更适合国内网络环境,而且是订阅助手式的多轮交互。需要配置的参数:
| 参数键 | 说明 |
|---|---|
openlist.wecom.corpid |
企业 ID,留空则企微功能整体不启用 |
openlist.wecom.agentid |
自建应用 AgentId |
openlist.wecom.secret |
自建应用 Secret |
openlist.wecom.token |
回调 Token,不配则只能发通知、收不到指令 |
openlist.wecom.aeskey |
回调 EncodingAESKey(43 位) |
openlist.wecom.touser |
无归属通知的接收人,默认 @all |
openlist.wecom.proxy |
API 代理地址,默认官方地址 |
openlist.wecom.autocreate |
成员首次发指令时自动建号绑定,默认开启 |
💡 2022-06-20 之后创建的自建应用调用企微 API 必须登记「企业可信 IP」,家宽/动态 IP 登记不了。通行做法是自己反代
qyapi.weixin.qq.com,把中转地址填进openlist.wecom.proxy。
支持的指令:
订阅 <剧名> 搜索剧集并订阅,如:订阅 三体
订阅电影 <片名> 搜索电影并订阅
我的订阅 查看自己的订阅列表
下载中 查看正在下载的集
最近入库 查看最近入库的集
进度 <编号> 查看某条订阅的进度
补搜 <编号> 立即搜索该订阅的全部缺集
诊断 <编号> 看看缺的集为什么还没下到
重试下载 <记录号> 重新搜索并下载一条失败的下载记录
拉黑种子 <记录号> 拉黑该下载记录对应的种子,以后不再推送
暂停 <编号> 暂停订阅
恢复 <编号> 恢复订阅
我的账号 查看绑定状态
取消 中断当前的多轮选择
帮助 显示本说明
搜索后直接回复序号即可选择。订阅是带归属的:某条订阅的动态只推给它的所有者,不会把 A 的下载进度推到 B 的企微上(历史遗留的无归属订阅退化为广播,所有人可见)。
7.4 通用 Webhook / Bark / Gotify
| 渠道 | 参数键 | 说明 |
|---|---|---|
| Webhook | openlist.notify.webhook.url |
OSR 会 POST 一个 {"text": "消息内容"} 过去 |
| Bark | openlist.notify.bark.url |
形如 https://api.day.app/你的Key,自建服务填自己的域名 |
| Gotify | openlist.notify.gotify.url + openlist.notify.gotify.token |
服务地址不含路径;两项都填了才会发送 |
三者都只有一个投递目标,因此在路由里一律是广播。发送范围同样由通知路由决定,早期的 openlist.notify.*.types 参数已被路由表取代。
新增一个渠道只要实现
INotifier接口,配置页会自动多出一列——但渠道标识一旦发布就不能改,改了等于把用户已有的路由配置全丢。
7.5 MCP:让本地 AI 助理接管
OSR 内置了一个 MCP 服务端,端点是 http://你的地址/mcp。Claude Desktop、Cursor、Cherry Studio 这类支持 MCP 的 AI 助理连上之后,就能用自然语言问「这周有哪些剧更新了还没下到」「帮我订阅一下《xxx》第二季」「把 12 号订阅补搜一下」。
配置步骤:
- 系统管理 → MCP 令牌 里签发一个令牌,选好权限档。令牌明文只在签发时显示一次,库里只存哈希;
- 在 AI 助理里添加一个 Streamable HTTP 类型的 MCP 服务器,地址填
http://你的地址/mcp,请求头加Authorization: Bearer osr_mcp_xxx。
一共 33 个工具,按「做错了要花多大代价收回来」分三档:read(查订阅、进度、日历、下载记录)、write(建订阅、触发任务、补搜、重试)、admin(删订阅、重置集状态、拉黑发布组)。权限档和 OSR 自身的权限是「与」的关系——普通用户的令牌勾上 admin,照样删不掉别人的订阅。有一批操作任何档都没有对应工具:删网盘文件、删种、改索引器/下载器/参数设置、用户与角色。
几个使用上要知道的:
- 补搜这类要跑几十秒以上的工具会立即返回一个作业 ID,助理再用
get_job_status轮询结果,不会因为客户端 60 秒超时而重复触发; - 用自带的
nginx.conf部署时/mcp已经单独配好(关缓冲、放大读超时)。自己套了一层反代的话也要同样处理,否则症状是握手正常、工具列表也拿得到,只有跑得久一点的调用莫名断线。
7.6 AI 功能
在参数设置里配好 OpenAI 兼容接口(openlist.openai.*)之后,除了重命名的 AI 兜底识别,还有三处可用:
- 自然语言建过滤规则(见 6.4),生成的是草稿,不确认不落库;
- 种子标题 AI 兜底解析:
openlist.openai.pt-title-fallback设为 1 开启(默认关)。本地正则解析不出的标题在后台排队交给 AI,结果缓存,下次搜到同一个种子时生效,不会拖慢 RSS 和补搜; - 每周周报的点评(见 4.4)。
八、日常运维
8.1 备份
配置备份与恢复(系统管理 → 备份与恢复,仅 PC 端、仅管理员):把参数设置、通知路由、STRM/同步/重命名任务与分类规则、PT 下载器/索引器/媒体服务器、各类 PT 规则、黑名单、企微用户绑定和 PT 订阅导出成一个 JSON 文件。恢复时:
- 先逐项预览会新增、会更新、会跳过什么,确认后才写入;
- 只合并、不删除,按名称/路径对应;引用到的下载器、用户按名字换成本机的 id,找不到就跳过并提示;
- 密码、API Key 这类敏感字段默认不导出,恢复时空值保留本机现有的值;
- 订阅在后台逐条重建并按本机媒体库对账,不会触发补搜,免得几十条订阅一起撞站点限流。
备份文件由你自己下载保存,服务端不留副本——留在服务端的备份在「整台机器坏了」这个最需要它的场景里恰恰跟着一起没了。换机器、重装时它比整库 dump 更合适;想完整保留下载记录、执行日志等历史数据,还是用下面的数据库备份。
数据库:
docker exec osr-mysql mysqldump -u root -p"$MYSQL_ROOT_PASSWORD" osr > osr-backup.sql
文件(STRM、日志、上传)直接备份宿主机的 /data 目录即可。
8.2 升级
docker compose pull && docker compose up -d
自建镜像的话:拉代码 → mvn clean package -DskipTests → 前端 cd osr-web && npm run build → docker compose up -d --build。
升级前先备份数据库。建表和迁移脚本由后端启动时自动执行,不需要手动导入。
跨多个版本升级时,后端要跑完全部迁移才会转为 healthy,而前端的 depends_on 用的是 condition: service_healthy——这段时间前端不启动是正常的,不是升级失败。healthcheck 的 start_period 给了 180 秒。
⚠️ 自建前端镜像有个坑:
Dockerfile.frontend不是多阶段构建,它只是把osr-web/distCOPY 进 Nginx 镜像,不会自己跑npm run build。改了前端代码不先手动构建的话,COPY这层会命中 Docker 缓存,容器里跑的还是旧代码(构建日志里这行显示CACHED就是没生效的信号)。
只更新单个服务:
docker compose up -d --build --no-deps frontend
8.3 常见问题
服务起不来? 确认 Docker 正常、.env 已配置、MySQL 初始化完成(首次启动需要时间),然后看 /data/logs/sys-error.log。
改数据库密码? MYSQL_ROOT_PASSWORD 和 DB_PASSWORD 是独立的,改完要重建容器才生效。
改 JWT_SECRET? 至少 32 字节,改完重启后端,所有用户需重新登录。
前端访问不了 API? 依次检查后端容器是否运行、后端日志有无报错、端口映射是否正确、前后端分域时 CORS_ALLOWED_ORIGINS 有没有配。
定时任务不执行? 检查任务是否启用、Cron 表达式是否正确、日志里有无异常。
能配多个 OpenList 实例吗? 当前版本只支持单实例。
登录提示账号被锁定? 登录失败会同时按「账号」和「来源 IP」两个桶计数,任一超阈值就临时锁定(默认账号 5 次、IP 30 次、锁定 10 分钟,锁定时长同时也是失败次数的统计窗口)。等锁定时长过去自动解除,阈值在参数管理的 sys.login.* 三项里调,填 0 关闭。
后端一直 starting,前端根本没起来? 这是设计如此:前端的 depends_on 用的是 condition: service_healthy,后端没就绪前端就不启动,比「Nginx 起来了却一路 502」更容易定位。首次启动要跑完全部迁移,start_period 给了 180 秒;超过这个时间仍不健康,去看 /data/logs/sys-error.log。
新增了 SQL 迁移脚本却没执行? 这是开发向的坑:MysqlDdl.getSqlFiles() 是硬编码的文件名清单,不是目录扫描,新脚本必须手动追加到列表末尾。
九、写在最后
OSR 的定位很清楚:它不试图取代 Emby/Jellyfin,也不取代 OpenList,而是把「下载器 → 网盘 → STRM → 媒体库」这条链路上所有需要人肉盯着的环节接管过来,并且在每个环节留下可查询、可重试的记录。
如果你只是想要 STRM 生成,配好 OpenList 地址和 token,建一个 STRM 任务就能用;如果你想要全自动追剧,那就沿着「索引器 → 下载器 → 媒体服务器 → 过滤规则 → 订阅」的顺序一路配下来。两种用法都成立,中间的每一档也都成立。
- 项目地址:github.com/907739769/OSR
- 完整文档:项目 Wiki
评论区