fix(jellyfin): 选中外挂 ASS 字幕导致播放失败 - #3301
Open
GrahamZen wants to merge 2 commits into
Open
Conversation
外挂字幕经 MediaExtraFiles.subtitles 交给播放器, Android 端把 Subtitle.mimeType 原样
喂给 ExoPlayer 的 MediaItem.SubtitleConfiguration.setMimeType. 而 getSubtitles 把 ASS
映射成了 "text/x-ass", 这不是 media3 认识的类型: DefaultSubtitleParserFactory 的白名单里
ASS/SSA 对应 TEXT_SSA ("text/x-ssa"), ass-media 的 AssSubtitleParserFactory 也是判
sampleMimeType == TEXT_SSA 才走 libass. 于是退回旧 decoder 路径, TextRenderer 在
onStreamChanged 里抛 "Legacy decoding is disabled, can't handle text/x-ass samples"
—— 不是字幕不显示, 是整个播放挂掉. srt/vtt 则直接落进 application/octet-stream.
内封字幕不受影响 (由 MatroskaExtractor 按 codec id 填 sampleMimeType), 所以这个问题只在
媒体库里存在外挂字幕文件时才会暴露.
- ass/ssa -> text/x-ssa, srt/subrip -> application/x-subrip, vtt/webvtt -> text/vtt.
Jellyfin 的 Codec 字段透传 ffprobe 的 codec name, 所以 SRT 报的是 "subrip" 而不是 "srt".
- Codec 先转小写再拼进字幕 URL 的扩展名.
- label 在没有 Title 时依次退到 Language 和占位符 —— Jellyfin 对只有语言、没有标题的外挂
字幕不给 Title, 此时字幕列表里显示的是 ExoPlayer 的 TrackGroup id (实测显示成 "1:0").
三个回归测试分别守: MIME 映射、URL 扩展名小写、label 兜底.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GrahamZen
force-pushed
the
fix/jellyfin-subtitle-mime-types
branch
from
August 13, 2026 21:04
a205551 to
3070ef2
Compare
Member
|
其他播放器呢,最少要再测试 mpv |
Contributor
|
已测试桌面端 mpv,主视频播放正常、未发现回归。由于当前 mpv 后端尚未消费 MediaExtraFiles.subtitles,本 PR 对 mpv 没有可观察的行为影响。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
选中 Jellyfin 的外挂 ASS 字幕(
MediaStreams里IsExternal == true的流)会让播放直接失败:外挂字幕经由
MediaExtraFiles.subtitles交给播放器,Android 端把Subtitle.mimeType原样喂给 ExoPlayer 的MediaItem.SubtitleConfiguration.setMimeType(LibassExoPlayerMediampPlayer.createLibassMediaSource)。而BaseJellyfinMediaSource.getSubtitles把 ASS 映射成了"text/x-ass"—— 这不是 media3 认识的类型,两条解析路径都拒绝它:DefaultSubtitleParserFactory.supportsFormat的白名单里 ASS/SSA 对应的是TEXT_SSA = "text/x-ssa",没有text/x-ass。ass-media的AssSubtitleParserFactory.supportsFormat直接委托给它,create里也是format.sampleMimeType == MimeTypes.TEXT_SSA才走 libass。TextRenderer在onStreamChanged里抛出上面那个IllegalStateException(media3 的源码注释写明这是故意让 App "aware via the playback failure")。srt / vtt 则直接落进
application/octet-stream。内封字幕不受影响:它们由
MatroskaExtractor按 codec id 填Format.sampleMimeType,不经过这段代码 —— 所以这个问题只在媒体库里存在外挂字幕文件时才会暴露。顺带两个小问题:
getSubtitleUri用原始Codec拼字幕 URL 的扩展名(Stream.$codec),而 Jellyfin 是按这个扩展名决定输出格式的,服务器返回大写(如"ASS")时就不可靠。label = stream.Title,但Title是可空的。Jellyfin 对只有语言、没有标题的外挂字幕就不给Title,此时字幕列表里显示的是 ExoPlayer 的 TrackGroup id(实测显示成1:0)。改动
ass/ssa->text/x-ssa,srt/subrip->application/x-subrip,vtt/webvtt->text/vtt。带上subrip/webvtt是因为 Jellyfin 的Codec字段透传的是 ffprobe 的 codec name,SRT 报的是subrip而不是srt。Codec先转小写再拼进字幕 URL。label在没有Title时依次退到Language和占位符。验证
新增
JellyfinSubtitleStreamTest,3 个测试分别守 MIME 映射、URL 扩展名小写、label 兜底。响应字段照着仓库里的真实响应样本datasource/jellyfin/sample-item.json写,并额外放了一条PGSSUB图形字幕和一条音轨,确认它们仍被IsTextSubtitleStream/Type过滤掉。做了对照实验(同一套测试,只换生产代码):
main原样(git checkout main -- BaseJellyfinMediaSource.kt)测试执行:
./gradlew :datasource:jellyfin:desktopTest --rerun-tasks,13 个测试全过。实机验证(Android TV + 真实 Jellyfin):给一集 mkv 旁边放一个外挂
.ass(Jellyfin 识别为IsExternal: true/Codec: "ass",内容是几条带\pos定位、颜色、\frz旋转和\move动画的特效行),在字幕列表里选中这条外挂轨:can't handle text/x-ass samples;该轨在列表里显示成1:0ass->text/x-ssa与label兜底)zho未覆盖:srt / vtt 的映射没有实机验证(手上的服务器只有 ass 外挂字幕),是按 media3
MimeTypes的常量对齐补的;桌面端未实机运行。🤖 Generated with Claude Code