fix(media): 实现媒体反代并修复下载崩溃,新增 GIF 大小限制压缩#77
Conversation
反代(修复严重回归): - #74 给 base_sender 加了反代管线,把 image_relay_base_url/media_relay_base_url 传给 MediaDownloader.get_or_download(_prepared),但下载器从未接收这两个参数, 导致每次媒体下载 TypeError 被吞、download_failed=True,所有媒体静默发不出去。 - 在 get_or_download / get_or_download_prepared 加这两个参数并真正接通:新增 _build_relay_url(支持 https://wsrv.nl/ 与 https://wsrv.nl/?url= 两种形式)和 _select_relay_base(图片优先图片反代、非图片走通用反代),在抓取处"先反代再 回源"。缓存 key 与 original_url 始终保持原始 URL,m3u8 不走反代,两个反代为空 时行为与之前一致。 ffmpeg: - 新增 FFmpegTool.transcode_to_gif_under_limit,逐步降分辨率/帧率把视频转为不超过 指定字节数的 GIF;media_downloader 在生成 GIF 变体时调用。 测试: - 新增反代回归测试与 GIF 压缩测试;修正 test_base_sender_ffmpeg 中因 #74 起就过时 的预期 call dict(补 relay 键)。
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
审阅者指南在 media_downloader 中端到端实现媒体反向代理(中继)支持,修复了由于意外的中继 kwargs 导致媒体无法发送的回归问题,并通过 ffmpeg 新增了带大小限制的 GIF 压缩流程以及相应测试,并调整了 sender 侧的预期。 带中继与回退逻辑的媒体下载时序图sequenceDiagram
actor Sender
participant MediaDownloader
participant _select_relay_base
participant _build_relay_url
participant download_to_temp
Sender->>MediaDownloader: get_or_download_prepared(..., image_relay_base_url, media_relay_base_url)
MediaDownloader->>MediaDownloader: get_or_download(..., image_relay_base_url, media_relay_base_url)
MediaDownloader->>_select_relay_base: _select_relay_base(media_type, image_relay_base_url, media_relay_base_url)
_select_relay_base-->>MediaDownloader: relay_base
MediaDownloader->>_build_relay_url: _build_relay_url(relay_base, url)
_build_relay_url-->>MediaDownloader: fetch_url
alt fetch_url != url
MediaDownloader->>download_to_temp: download_to_temp(url=fetch_url, ...)
opt relay download fails
MediaDownloader->>download_to_temp: download_to_temp(url=url, ...)
end
else fetch_url == url
MediaDownloader->>download_to_temp: download_to_temp(url=url, ...)
end
MediaDownloader-->>Sender: PreparedMedia
文件级变更
可能关联的问题
提示与命令与 Sourcery 交互
自定义你的体验访问你的 控制台 以:
获取帮助Original review guide in EnglishReviewer's GuideImplements media reverse-proxy (relay) support end-to-end in the media downloader, fixes a regression where media could not be sent due to unexpected relay kwargs, and adds a size-limited GIF compression path via ffmpeg with corresponding tests and adjusted sender expectations. Sequence diagram for media download with relay and fallbacksequenceDiagram
actor Sender
participant MediaDownloader
participant _select_relay_base
participant _build_relay_url
participant download_to_temp
Sender->>MediaDownloader: get_or_download_prepared(..., image_relay_base_url, media_relay_base_url)
MediaDownloader->>MediaDownloader: get_or_download(..., image_relay_base_url, media_relay_base_url)
MediaDownloader->>_select_relay_base: _select_relay_base(media_type, image_relay_base_url, media_relay_base_url)
_select_relay_base-->>MediaDownloader: relay_base
MediaDownloader->>_build_relay_url: _build_relay_url(relay_base, url)
_build_relay_url-->>MediaDownloader: fetch_url
alt fetch_url != url
MediaDownloader->>download_to_temp: download_to_temp(url=fetch_url, ...)
opt relay download fails
MediaDownloader->>download_to_temp: download_to_temp(url=url, ...)
end
else fetch_url == url
MediaDownloader->>download_to_temp: download_to_temp(url=url, ...)
end
MediaDownloader-->>Sender: PreparedMedia
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我在这里给出一些整体性的反馈:
- 关于 relay 的辅助函数签名(
_select_relay_base、_build_relay_url,以及新的image_relay_base_url/media_relay_base_url参数)目前假设类型为str,但在内部是通过or ''来处理可能的None;建议将类型标注调整为str | None(以及/或者在对外的边界处做一次规范化),这样能更准确地反映实际用法,也能避免调用方和类型检查器产生困惑。 - 在
FFmpegTool.transcode_to_gif_under_limit中,你在循环内多次调用output_path.stat()(包括在已有缓存文件的快速路径上);在每次尝试时只缓存一次文件大小可以简化逻辑,并避免重复的系统调用。
给 AI Agent 的提示
请根据这次代码审查中的评论进行修改:
## 总体评论
- 关于 relay 的辅助函数签名(`_select_relay_base`、`_build_relay_url`,以及新的 `image_relay_base_url` / `media_relay_base_url` 参数)目前假设类型为 `str`,但在内部是通过 `or ''` 来处理可能的 `None`;建议将类型标注调整为 `str | None`(以及/或者在对外的边界处做一次规范化),这样能更准确地反映实际用法,也能避免调用方和类型检查器产生困惑。
- 在 `FFmpegTool.transcode_to_gif_under_limit` 中,你在循环内多次调用 `output_path.stat()`(包括在已有缓存文件的快速路径上);在每次尝试时只缓存一次文件大小可以简化逻辑,并避免重复的系统调用。帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进之后的代码审查。
Original comment in English
Hey - I've left some high level feedback:
- The helper signatures around relays (
_select_relay_base,_build_relay_url, and the newimage_relay_base_url/media_relay_base_urlparameters) assumestrbut internally handle potentialNoneviaor ''; consider adjusting the type hints tostr | None(and/or normalizing once at the public boundary) to better reflect actual usage and avoid confusion for callers and type-checkers. - In
FFmpegTool.transcode_to_gif_under_limit, you calloutput_path.stat()multiple times inside the loop (including on the fast path where a cached file already exists); caching the size once per attempt would simplify the logic and avoid redundant syscalls.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The helper signatures around relays (`_select_relay_base`, `_build_relay_url`, and the new `image_relay_base_url` / `media_relay_base_url` parameters) assume `str` but internally handle potential `None` via `or ''`; consider adjusting the type hints to `str | None` (and/or normalizing once at the public boundary) to better reflect actual usage and avoid confusion for callers and type-checkers.
- In `FFmpegTool.transcode_to_gif_under_limit`, you call `output_path.stat()` multiple times inside the loop (including on the fast path where a cached file already exists); caching the size once per attempt would simplify the logic and avoid redundant syscalls.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Pull request overview
该 PR 修复了媒体发送的严重回归:此前 base_sender 通过 **relay_kwargs 传入的 image_relay_base_url / media_relay_base_url 未被 MediaDownloader.get_or_download(_prepared) 接收,导致每次下载抛 TypeError 并被吞掉,媒体静默丢失。同时补齐了 GIF 变体的大小限制压缩能力。
Changes:
- 在
MediaDownloader中接通图片/通用媒体反代参数,新增反代 URL 构建与反代基址选择逻辑,并实现“先反代抓取、失败回源直连”的下载策略(m3u8 分支不走反代,缓存 key 保持原始 URL)。 - 在
FFmpegTool中新增transcode_to_gif_under_limit,通过逐步降分辨率/帧率生成不超过上限字节数的 GIF,并在生成 GIF 变体时启用压缩变体(compressed_gif)。 - 新增/更新单元测试:覆盖反代 base 形式、图片/非图片选择、反代失败回源、prepared 透传,以及更新 ffmpeg sender 相关的预期调用参数。
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tests/unit/infrastructure/test_media_downloader.py | 新增反代行为测试用例,覆盖 URL 构建、选择策略与回源逻辑,并验证缓存 key/original_url 不变。 |
| tests/unit/infrastructure/test_base_sender_ffmpeg.py | 更新 prepare_media 相关测试预期,补齐 relay 参数透传的 call dict。 |
| src/infrastructure/utils/ffmpeg_helper.py | 新增大小限制 GIF 转码工具 transcode_to_gif_under_limit(多档参数尝试 + 缓存)。 |
| src/infrastructure/media/media_downloader.py | 接入 image_relay_base_url/media_relay_base_url 到下载流程,新增 _build_relay_url / _select_relay_base,并在 GIF 变体生成中引入压缩变体。 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- relay 辅助函数与下载器参数(_build_relay_url / _select_relay_base / image_relay_base_url / media_relay_base_url)类型标注改为 str | None, 如实反映内部 `or ""` 容忍 None 的用法。 - transcode_to_gif_under_limit 每次尝试只读取一次 output_path.stat().st_size (缓存快速路径与转码后校验各缓存一次),减少重复 syscall。
修复一个严重回归:所有媒体(图片/视频/音频)都发不出去。并补上 GIF 大小限制压缩。
prepare_media报MediaDownloader.get_or_download_prepared() got an unexpected keyword argument 'image_relay_base_url'。根因是 #74 给base_sender加了反代管线,把image_relay_base_url/media_relay_base_url通过**relay_kwargs传给下载器,但MediaDownloader.get_or_download(_prepared)从未接收这两个参数 → 每次下载抛TypeError被 except 吞掉、download_failed=True,媒体静默丢失。反代功能虽有 config + 文档,但下载器侧实现从未写过。Modifications / 改动点
src/infrastructure/media/media_downloader.py:给get_or_download与get_or_download_prepared增加image_relay_base_url/media_relay_base_url参数并真正接通。_build_relay_url(支持https://wsrv.nl/与https://wsrv.nl/?url=两种形式)和_select_relay_base(图片优先图片反代,非图片走通用媒体反代,都未配置则不反代)。original_url始终保持原始 URL;m3u8 路径不走反代;两个反代为空时行为与之前逐字节一致。src/infrastructure/utils/ffmpeg_helper.py:新增FFmpegTool.transcode_to_gif_under_limit,逐步降分辨率/帧率把视频转为不超过指定字节数的 GIF;由 media_downloader 在生成 GIF 变体时调用。测试:新增反代回归测试(各种 base 形式、图片/非图片选择、先反代再回源、both-empty、prepared 透传)与 GIF 压缩测试;修正
test_base_sender_ffmpeg中自 fix(db,onebot): drop legacy link_preview column and add napcat stream upload #74 起就过时的预期 call dict(补 relay 键)。This is NOT a breaking change. / 这不是一个破坏性变更。
Screenshots or Test Results / 运行截图或测试结果
新增的反代测试断言
download_to_temp实际拿到的是反代 URL、而缓存 key /original_url仍为原始 URL;并覆盖反代失败回源。Checklist / 检查清单
Summary by Sourcery
通过将反向代理支持接入媒体下载器来修复媒体下载的回归问题,并为生成的 GIF 变体添加带大小限制的 GIF 压缩功能。
Bug 修复:
增强功能:
测试:
Original summary in English
Summary by Sourcery
Fix media downloading regression by wiring reverse-proxy support into the media downloader and add size-limited GIF compression for generated GIF variants.
Bug Fixes:
Enhancements:
Tests: