[PR #16161] fix: Playbackinfo - Return API URLs for local external subtitle files on remote media sources #14544

Open
opened 2026-02-07 07:31:31 +03:00 by OVERLORD · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/jellyfin/jellyfin/pull/16161
Author: @ashktn
Created: 2/1/2026
Status: 🔄 Open

Base: masterHead: fix/strm-files-with-local-srt


📝 Commits (2)

  • 8daa6f7 fix: Return API URLs for local external subtitles on remote media sources
  • 0593a50 fix: external subtitle delivery for remote media sources

📊 Changes

4 files changed (+144 additions, -2 deletions)

View changed files

📝 MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs (+11 -1)
📝 MediaBrowser.Model/Dlna/StreamInfo.cs (+2 -1)
📝 tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs (+29 -0)
📝 tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs (+102 -0)

📄 Description

fixes #16184

Issue Description

When a client calls the /Items/{itemId}/PlaybackInfo endpoint with a device profile that declares external subtitle support (e.g., SRT), the server incorrectly returns local file system paths for external subtitle files when the media source is remote.

Reproduction Steps

  1. Setup: Add an strm file in C:\Jellyfin\video\video.strm, which has a link to an http server path http://server.com/video.mkv

  2. Subtitle: External subtitle file stored locally on Jellyfin server's file system
    Path: C:\Jellyfin\video\subtitle.srt

  3. API Call: Client calls POST /Items/{id}/PlaybackInfo with device profile:
    SubtitleProfiles: [
    {Format: srt, Method: External},
    {Format: subrip, Method: External}
    ]

Expected Behavior

Server should return API URL for subtitle:
DeliveryUrl: /Videos/{itemId}/{mediaSourceId}/Subtitles/0/0/Stream.subrip?ApiKey={token}

Actual Behavior

Server returns local file path that client cannot access:
DeliveryUrl: C:\Jellyfin\video\subtitle.srt

Impact

  • Client receives HTTP 404 when trying to load subtitle

Fix

Added check: && MediaSource.Protocol == MediaProtocol.File
This ensures direct subtitle paths are only used when both media and subtitles are local.

Test

curl 'http://10.0.0.100:8096/Items/xxx/PlaybackInfo' \
  --data \
-raw '{"UserId":"123","StartTimeTicks":0,"IsPlayback":true,"AutoOpenLiveStream":true,"AudioStreamIndex":2,"SubtitleStreamIndex":0,"MediaSourceId":"8e4c8e9dc6ecd115fcb4c75207ad2501","MaxStreamingBitrate":416455717,"AlwaysBurnInSubtitleWhenTranscoding":false,"DeviceProfile":{"Name":"mpv","MaxStreamingBitrate":457857747,"DirectPlayProfiles":[{"Container":"mkv,mp4,webm,avi,mov,ts","Type":"Video","VideoCodec":"h264,hevc,vp8,vp9,av1,mpeg4,mpeg2video","AudioCodec":"aac,mp3,ac3,eac3,opus,flac,vorbis,mp2,pcm_s16le,pcm_s24le,pcm_dvd,wavpack,alac,truehd,dts"},{"Container":"mkv","Type":"Video","VideoCodec":"h264,hevc,vp8,vp9,av1","AudioCodec":"aac,opus,flac,wav,ogg,mp3"}],"TranscodingProfiles":[{"Container":"ts","Type":"Video","VideoCodec":"hevc,h264","AudioCodec":"aac,opus,flac","Context":"Streaming","Protocol":"hls","MaxAudioChannels":"6","MinSegments":"2","BreakOnNonKeyFrames":true,"EnableSubtitlesInManifest":true}],"SubtitleProfiles":[{"Format":"vtt","Method":"External"},{"Format":"webvtt","Method":"External"},{"Format":"srt","Method":"External"},{"Format":"subrip","Method":"External"},{"Format":"ttml","Method":"External"},{"Format":"ass","Method":"External"},{"Format":"ssa","Method":"External"}]}}' 

Before

{
  "MediaSources": [
    {
      "Protocol": "Http",
      "Id": "1234",
      "Path": "http://server/file.mkv",
      "Type": "Grouping",
      "Container": "mkv",
      "Size": 69,
      "Name": "Movie Name", // ...
      "MediaStreams": [
        {
          "Codec": "subrip",
          "Language": "eng",
          "TimeBase": "1/1000",
          "VideoRange": "Unknown",
          "VideoRangeType": "Unknown",
          "AudioSpatialFormat": "None",
          "LocalizedUndefined": "Undefined",
          "LocalizedDefault": "Default",
          "LocalizedForced": "Forced",
          "LocalizedExternal": "External",
          "LocalizedHearingImpaired": "Hearing Impaired",
          "DisplayTitle": "English - SUBRIP - External",
          "IsInterlaced": false,
          "IsAVC": false,
          "IsDefault": false,
          "IsForced": false,
          "IsHearingImpaired": false,
          "Height": 0,
          "Width": 0,
          "Type": "Subtitle",
          "Index": 0,
          "IsExternal": true,
          "DeliveryMethod": "External",
          "DeliveryUrl": "E:\\Jellyfin\\movie.en.srt", // <-- local path that the client cannot access
          "IsExternalUrl": true,
          "IsTextSubtitleStream": true,
          "SupportsExternalStream": true,
          "Path": "E:\\Jellyfin\\movie.en.srt",
          "Level": 0
        },

After

{
  "MediaSources": [
    {
      "Protocol": "Http",
      "Id": "1234",
      "Path": "http://server/file.mkv",
      "Type": "Grouping",
      "Container": "mkv",
      "Size": 69,
      "Name": "Movie Name", // ...
      "MediaStreams": [
        {
          "Codec": "subrip",
          "Language": "eng",
          "TimeBase": "1/1000",
          "VideoRange": "Unknown",
          "VideoRangeType": "Unknown",
          "AudioSpatialFormat": "None",
          "LocalizedUndefined": "Undefined",
          "LocalizedDefault": "Default",
          "LocalizedForced": "Forced",
          "LocalizedExternal": "External",
          "LocalizedHearingImpaired": "Hearing Impaired",
          "LocalizedLanguage": "English",
          "DisplayTitle": "English - SUBRIP - External",
          "IsInterlaced": false,
          "IsAVC": false,
          "IsDefault": false,
          "IsForced": false,
          "IsHearingImpaired": false,
          "Height": 0,
          "Width": 0,
          "Type": "Subtitle",
          "Index": 0,
          "IsExternal": true,
          "DeliveryMethod": "External",
          // after: api url
          "DeliveryUrl": "/Videos/425eea6c-c01d-edc0-2ac5-77465fc0a345/8e4c8e9dc6ecd115fcb4c75207ad2501/Subtitles/0/0/Stream.subrip?ApiKey=2788275b457b4dfd8f4cfaa09333c774",
          "IsExternalUrl": false,
          "IsTextSubtitleStream": true,
          "SupportsExternalStream": true,
          "Path": "E:\\Jellyfin\\movie.en.srt",
          "Level": 0
        }, //..


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/jellyfin/jellyfin/pull/16161 **Author:** [@ashktn](https://github.com/ashktn) **Created:** 2/1/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix/strm-files-with-local-srt` --- ### 📝 Commits (2) - [`8daa6f7`](https://github.com/jellyfin/jellyfin/commit/8daa6f79cf47d93cc455dc4d1f20bf9c0f27ca9e) fix: Return API URLs for local external subtitles on remote media sources - [`0593a50`](https://github.com/jellyfin/jellyfin/commit/0593a50bdfb6bf628758f4c85c5edbe369a45341) fix: external subtitle delivery for remote media sources ### 📊 Changes **4 files changed** (+144 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs` (+11 -1) 📝 `MediaBrowser.Model/Dlna/StreamInfo.cs` (+2 -1) 📝 `tests/Jellyfin.MediaEncoding.Tests/Subtitles/SubtitleEncoderTests.cs` (+29 -0) 📝 `tests/Jellyfin.Model.Tests/Dlna/StreamInfoTests.cs` (+102 -0) </details> ### 📄 Description fixes #16184 ## Issue Description When a client calls the /Items/{itemId}/PlaybackInfo endpoint with a device profile that declares external subtitle support (e.g., SRT), the server incorrectly returns local file system paths for external subtitle files when the media source is remote. ### Reproduction Steps 1. Setup: Add an `strm` file in `C:\Jellyfin\video\video.strm`, which has a link to an http server path `http://server.com/video.mkv` 2. Subtitle: External subtitle file stored locally on Jellyfin server's file system Path: `C:\Jellyfin\video\subtitle.srt` 3. API Call: Client calls `POST /Items/{id}/PlaybackInfo` with device profile: SubtitleProfiles: [ {Format: srt, Method: External}, {Format: subrip, Method: External} ] #### Expected Behavior Server should return API URL for subtitle: DeliveryUrl: /Videos/{itemId}/{mediaSourceId}/Subtitles/0/0/Stream.subrip?ApiKey={token} #### Actual Behavior Server returns local file path that client cannot access: DeliveryUrl: `C:\Jellyfin\video\subtitle.srt` ### Impact - Client receives HTTP 404 when trying to load subtitle ### Fix Added check: && MediaSource.Protocol == MediaProtocol.File This ensures direct subtitle paths are only used when both media and subtitles are local. ## Test ```bash curl 'http://10.0.0.100:8096/Items/xxx/PlaybackInfo' \ --data \ -raw '{"UserId":"123","StartTimeTicks":0,"IsPlayback":true,"AutoOpenLiveStream":true,"AudioStreamIndex":2,"SubtitleStreamIndex":0,"MediaSourceId":"8e4c8e9dc6ecd115fcb4c75207ad2501","MaxStreamingBitrate":416455717,"AlwaysBurnInSubtitleWhenTranscoding":false,"DeviceProfile":{"Name":"mpv","MaxStreamingBitrate":457857747,"DirectPlayProfiles":[{"Container":"mkv,mp4,webm,avi,mov,ts","Type":"Video","VideoCodec":"h264,hevc,vp8,vp9,av1,mpeg4,mpeg2video","AudioCodec":"aac,mp3,ac3,eac3,opus,flac,vorbis,mp2,pcm_s16le,pcm_s24le,pcm_dvd,wavpack,alac,truehd,dts"},{"Container":"mkv","Type":"Video","VideoCodec":"h264,hevc,vp8,vp9,av1","AudioCodec":"aac,opus,flac,wav,ogg,mp3"}],"TranscodingProfiles":[{"Container":"ts","Type":"Video","VideoCodec":"hevc,h264","AudioCodec":"aac,opus,flac","Context":"Streaming","Protocol":"hls","MaxAudioChannels":"6","MinSegments":"2","BreakOnNonKeyFrames":true,"EnableSubtitlesInManifest":true}],"SubtitleProfiles":[{"Format":"vtt","Method":"External"},{"Format":"webvtt","Method":"External"},{"Format":"srt","Method":"External"},{"Format":"subrip","Method":"External"},{"Format":"ttml","Method":"External"},{"Format":"ass","Method":"External"},{"Format":"ssa","Method":"External"}]}}' ``` ### Before ```jsonc { "MediaSources": [ { "Protocol": "Http", "Id": "1234", "Path": "http://server/file.mkv", "Type": "Grouping", "Container": "mkv", "Size": 69, "Name": "Movie Name", // ... "MediaStreams": [ { "Codec": "subrip", "Language": "eng", "TimeBase": "1/1000", "VideoRange": "Unknown", "VideoRangeType": "Unknown", "AudioSpatialFormat": "None", "LocalizedUndefined": "Undefined", "LocalizedDefault": "Default", "LocalizedForced": "Forced", "LocalizedExternal": "External", "LocalizedHearingImpaired": "Hearing Impaired", "DisplayTitle": "English - SUBRIP - External", "IsInterlaced": false, "IsAVC": false, "IsDefault": false, "IsForced": false, "IsHearingImpaired": false, "Height": 0, "Width": 0, "Type": "Subtitle", "Index": 0, "IsExternal": true, "DeliveryMethod": "External", "DeliveryUrl": "E:\\Jellyfin\\movie.en.srt", // <-- local path that the client cannot access "IsExternalUrl": true, "IsTextSubtitleStream": true, "SupportsExternalStream": true, "Path": "E:\\Jellyfin\\movie.en.srt", "Level": 0 }, ``` ### After ```jsonc { "MediaSources": [ { "Protocol": "Http", "Id": "1234", "Path": "http://server/file.mkv", "Type": "Grouping", "Container": "mkv", "Size": 69, "Name": "Movie Name", // ... "MediaStreams": [ { "Codec": "subrip", "Language": "eng", "TimeBase": "1/1000", "VideoRange": "Unknown", "VideoRangeType": "Unknown", "AudioSpatialFormat": "None", "LocalizedUndefined": "Undefined", "LocalizedDefault": "Default", "LocalizedForced": "Forced", "LocalizedExternal": "External", "LocalizedHearingImpaired": "Hearing Impaired", "LocalizedLanguage": "English", "DisplayTitle": "English - SUBRIP - External", "IsInterlaced": false, "IsAVC": false, "IsDefault": false, "IsForced": false, "IsHearingImpaired": false, "Height": 0, "Width": 0, "Type": "Subtitle", "Index": 0, "IsExternal": true, "DeliveryMethod": "External", // after: api url "DeliveryUrl": "/Videos/425eea6c-c01d-edc0-2ac5-77465fc0a345/8e4c8e9dc6ecd115fcb4c75207ad2501/Subtitles/0/0/Stream.subrip?ApiKey=2788275b457b4dfd8f4cfaa09333c774", "IsExternalUrl": false, "IsTextSubtitleStream": true, "SupportsExternalStream": true, "Path": "E:\\Jellyfin\\movie.en.srt", "Level": 0 }, //.. ``` --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
OVERLORD added the pull-request label 2026-02-07 07:31:31 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/jellyfin#14544