Seerr v3.2.0 与 v3.3.0 版本技术解读:Blocklist 升级、服务端 i18n 与通知体系增强
【免费下载链接】seerrOpen-source media request and discovery manager for Jellyfin, Plex, and Emby.项目地址: https://gitcode.com/GitHub_Trending/je/seerr
Seerr(面向 Jellyfin、Plex 与 Emby 的开源媒体请求与发现管理器)在 v3.2.0 与 v3.3.0 两个版本中集中发布了 Blocklist(屏蔽列表)能力升级、通知代理的服务端国际化(i18n)、Webhook 自定义请求头与 payload 增强,以及一系列质量改进。本文以官方发布说明为骨架,结合仓库源码逐项拆解这些新特性的配置方式与底层实现,帮助你理解如何在实际部署中启用并验证它们。
版本概览
本次发布说明合并覆盖了v3.2.0与v3.3.0两个版本。官方说明中提到,v3.2.0 发布时的说明被"遗漏",因此本次一并补上。两个版本以常规的缺陷修复与性能优化为基础,重点带来了以下四大方向的新能力:
- Blocklist 升级:支持按地区(Region)与语言(Language)过滤内容,并可将整个媒体合集(Collection)一键加入或移出屏蔽列表;
- 语言支持扩展:为所有通知代理引入服务端 i18n,通知在发送时按接收者语言自动翻译;正式新增爱沙尼亚语(Estonian)、卢森堡语(Luxembourgish)与越南语(Vietnamese)的完整界面支持;
- 通知与 Webhook 增强:Webhook 支持自定义请求头并在 payload 中注入
imdbid;ntfy 支持富 Markdown 与自定义优先级;Discord 支持多个用户/角色 ID; - 体验改进:Sonarr 的
monitorNewItems可开关、配额重置支持无限时间、用户列表可排序、Trending 页新增类型与时间窗口筛选、人物详情页新增 IMDb/TMDB 外链,并移除了"BETA 软件"横幅。
Blocklist 升级:按地区与语言过滤
配置项与含义
v3.2.0 在“设置 → 主设置”中为 Blocklist 引入了独立的地区与语言选项,见截图:
截图展示了两个下拉框:
- Blocklist Region(默认
All Regions):用于屏蔽列表内容扫描的地区; - Blocklist Language(默认
All Languages):用于屏蔽列表内容扫描的语言。
两个配置项在 server/lib/settings/index.ts 中定义为blocklistRegion与blocklistLanguage,与发现页的streamingRegion、originalLanguage并列存在。关键设计是二者"独立于发现页设置"(官方界面文案为independent of discover settings),这意味着你可以一边用某个地区/语言浏览发现页,一边用另一套地区/语言规则来判定哪些内容应被屏蔽,互不干扰。
底层实现:独立 TMDB 客户端
屏蔽列表扫描在构造 TheMovieDb 客户端时使用了专门的工厂函数。在 server/routes/discover.ts 中:
export const createTmdbWithBlocklistSettings = (): TheMovieDb => { const settings = getSettings(); return new TheMovieDb({ discoverRegion: settings.main.blocklistRegion, originalLanguage: settings.main.blocklistLanguage, }); };该客户端被 server/job/blocklistedTagsProcessor.ts 的定时任务使用——该任务读取settings.main.blocklistedTags(逗号分隔的关键词)与blocklistedTagsLimit,配合createTmdbWithBlocklistSettings()扫描 TMDB 并自动生成屏蔽条目。因此,地区/语言设置直接影响"按关键词自动屏蔽"的命中范围,这是 v3.2.0 把屏蔽粒度从单纯关键词扩展到地区与语言维度的核心改动。
Blocklist 升级:屏蔽整个媒体合集
操作方式与 API
除了单条内容,现在可以直接将整个合集(例如某个系列或系列电影)加入 Blocklist。前端在 Blocklist 页面提供合集屏蔽入口,后端对应两条新路由,位于 server/routes/blocklist.ts:
POST /api/v1/blocklist/collection/:id:拉取合集详情后,将其中每个成员以MediaType.MOVIE类型逐条写入 Blocklist,并同步把对应Media记录的状态置为BLOCKLISTED(4K 状态同样处理);DELETE /api/v1/blocklist/collection/:id:反向操作,遍历合集成员,移除 Blocklist 记录并删除对应的Media记录,返回204。
两条路由都使用dataSource.transaction(...)包裹全部写入/删除操作,保证批量处理的事务一致性。合集中已经屏蔽过的成员会被自动跳过(通过tmdbId判重,@Unique(['tmdbId', 'mediaType'])约束,见 server/entity/Blocklist.ts),因此重复操作不会产生冲突。
数据模型与标签支持
Blocklist 实体定义在 server/entity/Blocklist.ts,关键字段包括:
| 字段 | 类型 | 说明 |
|---|---|---|
tmdbId | number | 内容在 TMDB 的 ID,建索引 |
mediaType | 'movie' | 'tv' | 内容类型,与tmdbId组成唯一约束 |
title | varchar | 展示标题,列表搜索用 |
blocklistedTags | varchar | 触发屏蔽的关键词标签,可为空 |
user | 关联 User | 执行屏蔽操作的用户 |
createdAt | datetime | 创建时间,默认当前时间 |
Blocklist.addToBlocklist静态方法(server/entity/Blocklist.ts)封装了"写 Blocklist + 建/改 Media 状态"的完整流程:若Media尚不存在则创建一条状态为BLOCKLISTED的新记录,否则复用已有记录并更新状态。相关的表结构迁移可见 server/migration/sqlite/1771080196816-RenameBlacklistToBlocklist.ts(旧表改名)与 server/migration/sqlite/1737320080282-AddBlacklistTagsColumn.ts(新增标签列)。
列表查询接口GET /api/v1/blocklist(server/routes/blocklist.ts)支持take、skip分页,search(按标题模糊匹配)与filter参数(all/manual/blocklistedTags,用于区分手动屏蔽与关键词自动屏蔽),接口按createdAt倒序返回分页结果。
服务端国际化(i18n):通知按接收者语言自动翻译
发送时翻译机制
v3.2.0 为所有通知代理引入了服务端 i18n:通知不再使用写死的英文文案,而是在发送时依据接收者语言调用getIntl(locale)获取翻译上下文。以 ntfy 代理为例,server/lib/notifications/agents/ntfy.ts 中:
const intl = getIntl(settings.options.locale as AvailableLocale); ... message += `**${intl.formatMessage(globalMessages.requestedBy)}:** ${...}`;globalMessages来自 server/i18n/globalMessages.ts,翻译资源位于 server/i18n/locale(含en.json、de.json、zh-Hans.json等 40 余个语言文件)。Discord 代理(server/lib/notifications/agents/discord.ts)同样在buildEmbed中接收locale参数并调用intl.formatMessage生成"请求人""请求状态""待批准"等字段文案。
各代理的语言设置
- Discord、Slack、Gotify、ntfy:新增独立的
locale配置项,默认语言可在各自通知代理设置中选择; - Discord 专属开关:新增
useUserLocale(server/lib/settings/index.ts)。开启后,由于 Discord Webhook 发往频道而非单个用户,Seerr 会优先采用被通知用户的个人语言设置;未开启则回退到通知代理配置的默认语言。对应逻辑见 server/lib/notifications/agents/discord.ts:
const locale = settings.options.useUserLocale ? (payload.notifyUser?.settings?.locale as AvailableLocale) : (settings.options.locale as AvailableLocale);下面的截图是服务端翻译生效后的实际通知效果(测试通知以德语呈现):
新增完整界面语言
在服务端 i18n 之外,本版本还正式加入了三种完整界面语言支持:Estonian(爱沙尼亚语)、Luxembourgish(卢森堡语)与Vietnamese(越南语)。对应翻译文件可在 server/i18n/locale 中找到:et.json、lb.json、vi.json;前端界面语言资源位于 src/i18n/locale。
通知与 Webhook 增强
Webhook:自定义请求头
Webhook 代理(server/lib/notifications/agents/webhook.ts)现在支持一组自定义 HTTP 请求头。配置界面见截图:
底层逻辑:
- 若配置了
authHeader(Authorization 请求头),优先使用; - 遍历
settings.options.customHeaders({ key, value }[]数组,定义见 server/lib/settings/index.ts),对每个非空键值对写入请求头; - 为避免冲突,当键为
authorization且已存在authHeader时会跳过该自定义头,保证既有认证配置不被覆盖。
settings.options.customHeaders.forEach((header) => { const key = header.key?.trim(); const value = header.value?.trim(); if (key && value) { if (key.toLowerCase() !== 'authorization' || !settings.options.authHeader) { headers[key] = value; } } });Webhook:payload 注入 imdbid
Webhook 的 JSON payload 变量映射表(server/lib/notifications/agents/webhook.ts)新增了media_imdbid键,对应media.imdbId,与已有的media_tmdbid、media_tvdbid并列。在配置 JSON payload 时使用{{media_imdbid}}占位符即可在通知中携带 IMDb ID;URL 变量替换同样支持该键(supportVariables开启时)。
ntfy:富 Markdown 与优先级
ntfy 代理(server/lib/notifications/agents/ntfy.ts)现在发送markdown: true,并在消息中生成粗体标签与多行结构化文本(请求人、请求状态、评论人、问题类型/状态等)。同时新增priority配置项(server/lib/settings/index.ts),默认值为3(正常优先级),发送时原样写入 ntfy payload。此外还支持tags(逗号分隔的标签列表)、attach(嵌入海报图)与click(点击跳转媒体详情页)等可选字段。
Discord:多用户/角色 ID 提及
Discord 用户设置中的User IDs字段(discordIds)现在支持填写多个ID,见截图:
实现上,server/lib/notifications/agents/discord.ts 会:
- 收集被通知用户的全部
discordIds,用DISCORD_SNOWFLAKE_REGEX(定义于 server/constants/discord.ts)逐一校验合法性; - 对管理员通知场景,遍历所有启用 Discord 且配置了 ID 的用户并聚合提及;
- 将提及拼入
content,同时用allowed_mentions.users/allowed_mentions.roles精确控制可提及对象,防止滥用@everyone。
该字段的存储迁移见 server/migration/sqlite/1779783365432-AddDiscordIdsColumn.ts。另外,Discord 还支持通过webhookRoleId提及角色(渲染为<@&roleId>),并通过webhookThreadId将通知发送到指定帖子的线程(通过 Webhook URL 的thread_id查询参数实现,见 server/lib/notifications/agents/discord.ts)。
其他增强与质量改进
Sonarr 的 monitorNewItems 可配置
服务设置中新增monitorNewItems开关('all' | 'none',定义见 server/lib/settings/index.ts)。该值在媒体请求落地为 Sonarr 系列时传入添加参数(server/api/servarr/sonarr.ts),用于控制新季/新集是否自动监控。请求订阅器 server/subscriber/MediaRequestSubscriber.ts 中构建 Sonarr 系列对象时即包含monitorNewItems: sonarrSettings.monitorNewItems。
配额重置支持无限时间
用户配额(Quota)设置新增"无限"选项:将配额重置周期设为无限后,可借此实现"每用户全局请求数"之类的总量限制语义,而无需周期性重置计数。
用户列表排序
用户管理列表支持按姓名、邮箱、角色、请求数等字段排序,便于在大量用户中快速定位目标账户。
Trending 页筛选
发现页的 Trending 增加了两个筛选维度(src/components/Discover/Trending.tsx):
- 媒体类型:
all(全部)/movie(仅电影)/tv(仅剧集); - 时间窗口:
day(每日榜单)/week(每周榜单)。
筛选状态通过useDiscover('/api/v1/discover/trending', { mediaType, timeWindow })实时驱动列表刷新。
人物详情页外部链接
人物详情页新增了便捷的 IMDb 与 TMDB 链接。在 src/components/PersonDetails/index.tsx 中,通过ExternalLinkBlock组件传入tmdbId与imdbId渲染外部链接按钮:
<ExternalLinkBlock mediaType="person" tmdbId={data.id} imdbId={data.imdbId} />移除 BETA 横幅
Seerr 在 About 页面移除了"BETA software"横幅,正式宣告项目已度过 Beta 阶段。
升级与迁移说明
- 涉及表结构变更的迁移脚本位于 server/migration/sqlite 与 server/migration/postgres,升级时迁移器会自动执行,无需手工操作;
- 与本次特性直接相关的迁移包括:Blocklist 相关表结构与标签列、
discordIds列(SQLite 与 PostgreSQL 两侧均有对应版本); - 若需验证通知代理的翻译效果,可在各代理设置中发送测试通知(测试通知也会经过
getIntl渲染,便于快速确认语言配置); - Blocklist 的地区/语言设置与发现页设置相互独立,调整后由
blocklistedTagsProcessor定时任务按新参数重新扫描,因此修改配置后无需重启服务即可在下一次扫描周期生效。
从源码层面看,本次发布的核心架构动作可以总结为三条主线:Blocklist 从"关键词黑名单"进化为"地区 + 语言 + 关键词 + 合集"的多维屏蔽体系(server/routes/blocklist.ts、server/job/blocklistedTagsProcessor.ts);通知体系全面服务端 i18n 化(server/lib/notifications/agents);Webhook/Discord/ntfy 的协议级能力补强。如果你正在使用或准备评估 Seerr,v3.3.0 是目前功能完整度与国际化成熟度都比较均衡的版本。
【免费下载链接】seerrOpen-source media request and discovery manager for Jellyfin, Plex, and Emby.项目地址: https://gitcode.com/GitHub_Trending/je/seerr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考