拆解 Radarr 海报管线:一张电影海报从 TMDb 到本地磁盘的落盘链路
【免费下载链接】RadarrMovie organizer/manager for usenet and torrent users.项目地址: https://gitcode.com/GitHub_Trending/ra/Radarr
Radarr 海报墙里点开一部片子,海报位却是空白,日志飘着一行Couldn't download media cover。Radarr 是面向 Usenet 与 BitTorrent 用户的电影管理器,海报墙只是它的一根毛细血管。读完你能独立跑通这张海报从 TMDb 到本地磁盘的落盘链路,并定位下载卡在哪一环。
核心机制:MediaCoverService / MediaCoverProxy
Radarr 的海报管线由MediaCoverService牵头,MediaCoverProxy负责未入库电影的远程补位。整条链路不靠定时任务,全靠事件总线串起来。
事件驱动:MovieUpdatedEvent 触发 EnsureCovers
元数据刷新后,事件总线投递MovieUpdatedEvent,真正的落盘点就在它的处理器里:
// src/NzbDrone.Core/MediaCover/MediaCoverService.cs (L227-L232) public void HandleAsync(MovieUpdatedEvent message) { var updated = EnsureCovers(message.Movie); _eventAggregator.PublishEvent(new MediaCoversUpdatedEvent(message.Movie, updated)); }它先跑EnsureCovers把封面落盘,再广播MediaCoversUpdatedEvent。这样前端能即时刷新海报,不必轮询。上游RefreshMovieService拉新元数据、写入Images后发事件,下游就自动跟上。
去重判定:Content-Length 比对跳过下载
不是每张海报都要重下,CoverAlreadyExistsSpecification决定要不要动手:
// src/NzbDrone.Core/MediaCover/CoverAlreadyExistsSpecification.cs (L25-L35) public bool AlreadyExists(string url, string path) { if (!_diskProvider.FileExists(path)) return false; var headers = _httpClient.Head(new HttpRequest(url)).Headers; var fileSize = _diskProvider.GetFileSize(path); return fileSize == headers.ContentLength; }它不比文件内容,只拿本地大小对远程HEAD的Content-Length。相等即命中,跳过下载,省流量。代价是:远程同尺寸但内容变了时会误判为已存在。
多尺寸落盘:固定高度表 + 信号量限流
命中或下载完成后,进入EnsureResizedCovers,每种封面类型固定两档高度:
// src/NzbDrone.Core/MediaCover/MediaCoverService.cs (L177-L193 节选) case MediaCoverTypes.Poster: case MediaCoverTypes.Headshot: heights = new[] { 500, 250 }; break; case MediaCoverTypes.Banner: heights = new[] { 70, 35 }; break; // … 省略 Fanart / Screenshot 分支主文件再按高缩放成-500、-250后缀文件。缩放交给 ImageSharp,JPEG 质量写死 92。Semaphore把并发压到 CPU 核数一半,避免 ARM 上打满。
最小可行配置:从 clone 到看到第一张海报落盘
从 clone 到第一张海报落盘,核心就两条命令。
git clone https://gitcode.com/GitHub_Trending/ra/Radarr # 拉取源码 cd Radarr && dotnet run --project src/NzbDrone.Console # 启动,默认监听 7878 # 浏览器访问 7878,添加任意电影,即触发海报落盘 # AppData/MediaCover/<movieId>/ 生成 poster.jpg / poster-500.jpg / poster-250.jpg访问http://localhost:7878,添加一部电影,MovieUpdatedEvent就会驱动上面的管线。完整 Web 界面需另建前端,但落盘与下载逻辑不依赖它。
配置项速查:最常改的 4 个开关
海报墙最常动的是这几个开关,改之前先知道它们管什么。
| 配置入口 | 作用 | 推荐值 | 踩坑提示 |
|---|---|---|---|
config.xml的端口 /UrlBase | 决定监听端口与封面 URL 前缀 | 7878 | 改UrlBase后旧封面链接失效 |
MediaCover目录(数据目录下) | 海报落盘根目录 | 默认 | 直接删目录会导致全部重下 |
Kodi 的MovieImages | 是否把海报复制成poster.jpg到电影目录 | 开 | 关掉后 Kodi/Emby 读不到海报 |
ImageResizer的 JPEG 质量 | 缩略图编码质量 | 92(写死) | 要调质量得改源码,非配置项 |
踩坑实录
海报管线踩坑集中在下载、缩放、清理三处。
海报 404
现象:请求poster-500.jpg却拿到原尺寸图。根因:缩略文件缺失,控制器回退全尺寸。修复:确认数据目录可写,重启后自动重缩放。
一直不下载
现象:海报位长期空白,无报错弹窗。根因:TMDb 远程地址失效抛WebException。修复:看日志Couldn't download media cover,重下或刷新元数据。
删除后残留
现象:删掉电影,海报文件还在磁盘。根因:MoviesDeletedEvent只清理已识别目录。修复:手动清空MediaCover/<id>目录。
海报管线的核心是事件驱动加本地落盘:MovieUpdatedEvent触发下载,Content-Length去重,固定高度表产出多档尺寸。想进一步动手,直接读src/NzbDrone.Core/MediaCover/MediaCoverService.cs(L107-L216),从EnsureCovers顺着缩放分支往下走。
【免费下载链接】RadarrMovie organizer/manager for usenet and torrent users.项目地址: https://gitcode.com/GitHub_Trending/ra/Radarr
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考