Skip to content

media: relay an animated image document as an image - #9

Merged
std-microblock merged 1 commit into
std-microblock:mainfrom
std-external:fix/animated-image-kind
Oct 14, 2026
Merged

std-microblock merged 1 commit into
std-microblock:mainfrom
std-external:fix/animated-image-kind

Conversation

@std-external

Copy link
Copy Markdown
Contributor

问题

从 Telegram 侧发一个 GIF(没有选 send as file),到 QQ 群里落地成「文件」卡片,名字是客户端上传时的 <uuid>.gif;其它 Crossgram 客户端看到的也是 .gif file document。预期是 QQ 侧当动图/图片显示。

方向已确认是 TG → QQ。

原因

TG → QQ 的媒体类型只在 DialogRpc._resolveUploadedMedia() 里判一次:

const kind = media._ === 'inputMediaUploadedPhoto' ? 'image' : 'file'
  • 照片走 inputMediaUploadedPhoto → image;
  • 其它一律 file,包括以文档形式上传的动图。这里既没有看 mime,也没有看 documentAttributeAnimated,mimeType 在下面几行才被读出来用于其它字段。

于是 GIF 变成 kind: 'file',平台的媒体规格里也是 file,最后按普通文件上传/转发。同一个仓库在反方向(QQ → TG)是认得动图的——投影时会把这个文档投影成 kind: 'image' + image/gif(isAnimatedImageMime()),也就是说中继自己的模型里 GIF 就是图片,只有发送这一段没跟上。

修复

packages/bridge/src/dialogs.ts:把「客户端标了 documentAttributeAnimated 且 mime 是 image/gif / image/apng 的上传文档」也判成 image。

两个条件都要,是为了不改变现有语义:

  • 只认有 animated 属性的文档 ⇒ 显式「send as file」的 GIF、以及 sticker(走 sticker 通道)不受影响;
  • 同时要求 mime 是动图 mime ⇒ 转码成静音 mp4 的 GIF(video/mp4)不会被当成图片,仍然走平台既有的视频/文件判断。

验证

packages/bridge/src/media-send.test.ts 新增 3 个用例:

  • animated image/gif 文档 → 平台收到 kind: 'image';
  • 没有 animated 属性的 image/gif 文档(send as file)→ 仍然是 kind: 'file';
  • animated 的 video/mp4 文档 → 仍然是 kind: 'file'。
$ npx vitest run packages/bridge/src/media-send.test.ts
 ✓ packages/bridge/src/media-send.test.ts (24 tests) 1608ms

$ npx vitest run packages/bridge
 Test Files  1 failed | 55 passed (56)
       Tests 1 failed | 796 passed (797)

唯一的失败是 voice/voice-worker-client.test.ts(spawn cargo ENOENT,本机没有 cargo),与本次改动无关。

还需要确认的点

  1. QQ 图片上传的体积上限没有暴露在这个仓库里(qqnt-bridge 不在本工作区),所以这里没有加体积兜底。如果超大的动图在 QQ 图片通道会失败,需要按实际上限加一个退回文件的判断——我可以在拿到上限后补。
  2. 客户端侧还有一处相关的 kind:crossgram.prepareMediaUploadV3 的 kind 是客户端给的(Crossgram::FastUpload::Kind() 只把 photo 判成 image),只影响秒传命中的那条优化路径,需要改 desktop 补丁(另一个仓库),本 PR 不涉及。

来源:Koishi On Topic 群的实测反馈(「tg 发 gif 到 qq 发出来的是文件,应该是图片才对」)。

A GIF sent from a Telegram client arrives as an uploaded document, so the
relay classified it as a file and the platform relayed it as a plain
attachment: on QQ the user saw a "<uuid>.gif" file card for a message they
sent as an animation, and other Crossgram clients saw a file document too.

The relay already models an animated image document as an image in the
other direction, so send the same kind to the platform. Only a document
that carries both the animated image mime and documentAttributeAnimated
counts, which keeps an explicit "send as file" and a silent video on the
file path.
@std-microblock
std-microblock merged commit 7633b14 into std-microblock:main Oct 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants