ARRFLIX/docs/03-subtitles.md
s8n 937589c7a2 redact: scrub leaked Jellyfin admin API token from public repo
Token 76858153...f8b1 was committed across 9 docs + 1 snapshot RESTORE.md
and exposed via the brief public window of this repo. Replaced with
`<JELLYFIN_API_TOKEN>` placeholder.

WARNING: this commit only redacts HEAD — the token remains in git history.
Anyone who cloned during the public window has the full value. Treat the
old token as compromised and rotate at Jellyfin Dashboard > API Keys.
Original value backed up to private s8n/secrets/ARRFLIX/.
2026-05-08 15:36:14 +01:00

15 KiB
Raw Blame History

03 — Subtitles on Jellyfin (arrflix.s8n.ru)

Last updated: 2026-05-08 Server: Jellyfin 10.10.3 (X64) on nullstone, container jellyfin URL: https://arrflix.s8n.ru Use-case: Futurama (44 episodes), Polish audio, no embedded subs, automatic English subtitles required.


1. How Jellyfin resolves subtitles (priority order)

When a client requests playback, Jellyfin presents subtitle streams from three sources, in this order:

  1. Embedded — subtitle tracks muxed inside the container (mkv, mp4, etc.). Codecs: srt, ass, ssa, pgs, vobsub, dvd_subtitle, mov_text. Always available.
  2. Sidecar (external) — .srt, .vtt, .ass, .ssa, .sub files placed next to the video on disk. Discovered automatically on library scan.
  3. Downloaded — fetched at scan time (or via "Find subtitles" UI) by a subtitle provider plugin (OpenSubtitles, etc.). Saved as a sidecar when SaveSubtitlesWithMedia=true.

A user's SubtitleMode setting (per-user) decides what is auto-selected at playback:

  • None — never load subtitles by default.
  • Default — load only if marked IsDefault in the file.
  • Always — always load if any track in SubtitleLanguagePreference exists.
  • Smart — load only when audio language differs from the user's audio preference.
  • OnlyForced — load only forced tracks.

The s8n user is set to Always + preference eng (see § 6).


2. Current Futurama state (verified 2026-05-08)

Library:   TV Shows (Id 767bffe4f11c93ef34b805451a696a4e, path /media/tv)
Episodes:  44 (Futurama.s01e01.pl.mkv … s01e44? — actually s01s07 PL dub split)
Container: mkv
Audio:     ac3 stereo 192k, language=pol
Video:     h264 1080p (the eng tag on the video stream is a mux artefact, not English audio)
Subs:      0 embedded, 0 sidecar — verified on episodes 15; assume same for all 44

Result: every episode needs subs from OpenSubtitles or sidecars dropped on disk. There is nothing to extract — mkvextract/ffmpeg would yield nothing.


3. OpenSubtitles plugin — what is installed, what is pending

3.1 Installed (done by automation 2026-05-08)

Item Value
Plugin Open Subtitles v20.0.0.0
GUID 4b9ed42f-5185-48b5-9803-6ff2989014c4
Status Active (after docker restart jellyfin, ~5 s downtime)
Repo Official https://repo.jellyfin.org/files/plugin/manifest.json (already configured)
API endpoint https://api.opensubtitles.com/api/v1 (REST, NOT the legacy XML-RPC at .org)
API key Embedded in the plugin binary — user does NOT supply one

3.2 Why v20 and not v24

Plugin version targetAbi Compatible with 10.10.3?
v24, v23, v22, v21 10.11.x NO — ABI too new, will not load
v20 10.9.0.0 YES — installed
v19 and older 10.8.x yes but lacks recent fixes

When the server is upgraded to 10.11.x, switch to v24 via:

curl -s -X POST -H "X-Emby-Token: $TOKEN" \
  "https://arrflix.s8n.ru/Packages/Installed/Open%20Subtitles?AssemblyGuid=4b9ed42f-5185-48b5-9803-6ff2989014c4&Version=24.0.0.0&RepositoryUrl=https%3A%2F%2Frepo.jellyfin.org%2Ffiles%2Fplugin%2Fmanifest.json"
docker restart jellyfin

3.3 The .com vs .org distinction (READ THIS)

OpenSubtitles split into two services in 2021:

  • opensubtitles.org — legacy site, legacy XML-RPC API. Old Jellyfin plugin (v16 and below) used this. DEAD for new code.
  • opensubtitles.com — new site, new REST API. Jellyfin plugin v17+ (including the v20 we have) uses this exclusively.

User accounts: an opensubtitles.org account does NOT automatically work on .com. Existing .org users must:

  1. Visit https://www.opensubtitles.com/en/users/sign_up.
  2. Sign up with the same email — the system will offer to import the .org account.
  3. Reset password (mandatory; the .org password hash is incompatible).

User does NOT need to obtain an API key. The plugin embeds its own key (verified by reading OpenSubtitlesPlugin.ApiKey in RequestHandler.cs of v20). Free accounts get 20 downloads/day; VIP accounts get more.

3.4 Pending — user supplies credentials

After signup at opensubtitles.com, save creds via API:

TOKEN=<JELLYFIN_API_TOKEN>
USER='your-opensubtitles-com-username'
PASS='your-opensubtitles-com-password'

# 1. Validate (returns 200 on success, 401 on bad creds)
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
  -d "{\"Username\":\"$USER\",\"Password\":\"$PASS\"}" \
  "https://arrflix.s8n.ru/Jellyfin.Plugin.OpenSubtitles/ValidateLoginInfo" -w "\nHTTP %{http_code}\n"

# 2. Persist into plugin config
curl -s -X POST -H "X-Emby-Token: $TOKEN" -H "Content-Type: application/json" \
  -d "{\"Username\":\"$USER\",\"Password\":\"$PASS\",\"CredentialsInvalid\":false}" \
  "https://arrflix.s8n.ru/Plugins/4b9ed42f-5185-48b5-9803-6ff2989014c4/Configuration" -w "\nHTTP %{http_code}\n"

Or via UI: Dashboard → Plugins → Open Subtitles → Settings.

A failed validate currently returns {"Message":"Error, invalid username/password failed:5 remaining:5"} HTTP 401 — the embedded API key is fine, only the user creds are missing.


4. Library + user configuration (already applied)

4.1 Library options — TV Shows (and Movies)

POST /Library/VirtualFolders/LibraryOptions was issued with:

{
  "Id": "767bffe4f11c93ef34b805451a696a4e",
  "LibraryOptions": {
    "SubtitleDownloadLanguages": ["eng"],
    "RequirePerfectSubtitleMatch": false,
    "SkipSubtitlesIfAudioTrackMatches": true,
    "SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
    "SaveSubtitlesWithMedia": true,
    "AllowEmbeddedSubtitles": "AllowAll"
  }
}

Effect:

  • SubtitleDownloadLanguages: ["eng"] — primary trigger. Any new scan or "Refresh metadata" will fetch English subs for items lacking them.
  • RequirePerfectSubtitleMatch: false — accept good-match subs even when filename hashes don't line up (Polish-dub Futurama.s01e01.pl.mkv will not byte-match an English .srt anywhere on the planet).
  • SkipSubtitlesIfAudioTrackMatches: true — never fetch a language already present as an audio track (no English audio here, so no effect; safe to leave on).
  • SaveSubtitlesWithMedia: true — write .eng.srt next to the .mkv instead of caching in the metadata folder. Survives library wipes and is portable.

4.2 Per-user playback prefs — user s8n

POST /Users/2be0f0d3fe3a45dc9298138a15a01925/Configuration:

{
  "AudioLanguagePreference": "pol",
  "SubtitleLanguagePreference": "eng",
  "SubtitleMode": "Always"
}

Result: every Futurama episode auto-plays Polish audio + auto-loads English subs.

Other users (if any) need the same change individually — there is no global "default new user" subtitle prefs in 10.10.x; set on each user.


5. Triggering download for the existing 44 episodes

Adding SubtitleDownloadLanguages does NOT retroactively fetch subs for items already scanned. After credentials are saved, force a search:

5.1 Single episode (test path — episode Futurama.s01e01.pl)

EP=2b73bc176fbf8a02bb9bea9015ec13c6

# Query providers
curl -s -H "X-Emby-Token: $TOKEN" \
  "https://arrflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/eng" | jq .
# Returns array of SubtitleInfo objects: Id, ProviderName, Format, Comment, IsHashMatch, ...

# Pick one, e.g. SUBID = first result's Id
SUBID="opensubtitles_..."

# Download + save next to media
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
  "https://arrflix.s8n.ru/Items/$EP/RemoteSearch/Subtitles/$SUBID" -w "HTTP %{http_code}\n"
# 204 = saved. File appears as Futurama.s01e01.pl.eng.srt next to the mkv.

While unauthenticated, RemoteSearch returned [] — confirms credentials are the only blocker.

5.2 All 44 — refresh metadata for the whole series

Easiest is a series-level refresh once creds are entered:

SERIES=156e57437f795e5c8cd80fc98bafaee0  # Futurama
curl -s -X POST -H "X-Emby-Token: $TOKEN" \
  "https://arrflix.s8n.ru/Items/$SERIES/Refresh?MetadataRefreshMode=FullRefresh&ImageRefreshMode=Default&ReplaceAllMetadata=false&ReplaceAllImages=false&Recursive=true" \
  -w "HTTP %{http_code}\n"

MetadataRefreshMode=FullRefresh triggers subtitle download for items missing matching language. This will take a few minutes and is rate-limited by the OpenSubtitles 20-downloads/day cap on free accounts. 44 episodes exceeds that — plan for 3 days, or upgrade to VIP, or use sidecars (§ 7).


6. Per-user vs library default — who wins

Setting Lives in Effect
SubtitleDownloadLanguages (library) Library options What languages are fetched and saved to disk
SubtitleLanguagePreference (user) User config Which existing track is auto-selected at playback
SubtitleMode (user) User config When to auto-display (Always / Default / OnlyForced / Smart / None)
AudioLanguagePreference (user) User config Which audio track is auto-selected

A user's preferences are display-time only — they cannot trigger a download. A library's SubtitleDownloadLanguages is scan-time only — it does not affect what plays. You need both, and they should agree.


7. Sidecar subtitles — naming convention

If/when subs come from OpenSubtitles, Jellyfin saves them as sidecars (because SaveSubtitlesWithMedia=true). You can also drop your own. Filename pattern:

<videobasename>.<lang>[.flag].<ext>

Examples for Futurama.s01e01.pl.mkv:

Filename Meaning
Futurama.s01e01.pl.en.srt English, regular
Futurama.s01e01.pl.eng.srt English (ISO-639-2 code, also accepted)
Futurama.s01e01.pl.en.forced.srt English forced (only foreign-language scenes)
Futurama.s01e01.pl.en.sdh.srt English SDH (deaf/hard-of-hearing — includes [music], [door slams])
Futurama.s01e01.pl.en.cc.srt English closed captions (alias of SDH in Jellyfin)
Futurama.s01e01.pl.en.default.srt Marked default — auto-selects regardless of SubtitleMode
Futurama.s01e01.pl.en.forced.default.srt Forced AND default (flags compose, any order)
Futurama.s01e01.pl.en.ass Advanced SubStation Alpha — supports styling/positions
Futurama.s01e01.pl.en.ssa SubStation Alpha — older, also supported
Futurama.s01e01.pl.en.vtt WebVTT — supported
Futurama.s01e01.pl.en.sub + .idx VobSub (DVD bitmap subs)

Language codes: Jellyfin accepts both ISO-639-1 (en, pl, de) and ISO-639-2/T (eng, pol, deu). Either works for filename matching — the parser canonicalises to 639-2 internally.

Region tags like en-US, pt-BR are accepted and shown to the user but treated as the bare language for matching.

After dropping sidecars, trigger a library scan:

curl -s -X POST -H "X-Emby-Token: $TOKEN" "https://arrflix.s8n.ru/Library/Refresh"

Or per-item: POST /Items/{id}/Refresh.


8. Extracting embedded subs to sidecars (irrelevant for Futurama, but documented)

If a future series has embedded but slow-loading PGS/SSA subs, extract once with ffmpeg:

# List streams
ffmpeg -i input.mkv 2>&1 | grep Subtitle

# Extract stream index 2 (first sub) to .srt — works for srt/ass/mov_text
ffmpeg -i input.mkv -map 0:s:0 -c:s copy input.en.srt

# For PGS or VobSub bitmap subs, copy keeps them as-is (rename .sup):
ffmpeg -i input.mkv -map 0:s:0 -c:s copy input.en.sup
# Then OCR with subtitleedit-cli or pgs2srt to convert to text srt.

Or with mkvextract (mkvtoolnix-cli):

mkvmerge -i input.mkv      # lists tracks with IDs
mkvextract tracks input.mkv 2:input.en.srt

Jellyfin also has a built-in plugin Subtitle Extract (manifest GUID cd893c24-b59e-4060-87b2-184070e1bf68, latest v7.0.0.0 needs ABI 10.11.2.0 — wait until 10.11 upgrade). It extracts on the fly and caches in metadata/Subtitles/.


9. Auto-download settings cheatsheet

Field Where set Recommended value Why
SubtitleDownloadLanguages Library options ["eng"] Trigger downloads in English
RequirePerfectSubtitleMatch Library options false Don't insist on hash-match for foreign-dub releases
SkipSubtitlesIfEmbeddedSubtitlesPresent Library options false Allow override fetch even if embedded subs exist (e.g. if embedded are forced-only)
SkipSubtitlesIfAudioTrackMatches Library options true Don't fetch English subs for English audio (no waste)
SaveSubtitlesWithMedia Library options true Subs saved as sidecars on disk; portable; survives metadata wipe
AllowEmbeddedSubtitles Library options AllowAll Default; AllowText excludes PGS bitmap
MaxResults (n/a in v20 plugin config) The plugin returns the provider's full list; client picks
IsAutomated (n/a, removed) Older plugins had a flag; v20 always auto-fetches when a library scan finds an item missing the configured language

10. Troubleshooting

Symptom Cause / fix
RemoteSearch returns [] for every episode Creds missing / wrong (HTTP 401 in plugin logs). Re-validate via ValidateLoginInfo.
429 Too Many Requests in logs Hit the 20/day quota on free account. Wait 24 h, upgrade to VIP, or fall back to sidecars.
Subs found but in wrong language OpenSubtitles can mislabel — set RequirePerfectSubtitleMatch: true to filter, or pick manually via UI.
.srt on disk but Jellyfin doesn't show it Filename language token doesn't match. Use .en.srt not .english.srt. Trigger library scan.
Subs show but client doesn't auto-display User-side. Set SubtitleMode: Always and SubtitleLanguagePreference: eng.
Embedded subs preferred over downloaded Expected — embedded come first in the priority order. Use the player's track switcher, or remux without subs.
CredentialsInvalid: true keeps reappearing Plugin auto-flips this on a 401. Re-enter creds (likely changed on opensubtitles.com) and reset to false.
Plugin v24 install but stuck on Restart forever after upgrade Server still on 10.10.x — v24 needs ABI 10.11. Reinstall v20.

Plugin logs: docker logs jellyfin 2>&1 | grep -i opensubtitles.


11. Summary — Futurama after this work

Step State
Plugin Open Subtitles v20.0.0.0 Installed + Active
TV library SubtitleDownloadLanguages ["eng"]
TV library RequirePerfectSubtitleMatch false
TV library SaveSubtitlesWithMedia true
User s8n SubtitleMode Always
User s8n SubtitleLanguagePreference eng
User s8n AudioLanguagePreference pol
OpenSubtitles credentials PENDING — user signs up at https://www.opensubtitles.com
Series refresh to fetch all 44 PENDING — after creds entered

When the user enters creds and runs the series refresh in § 5.2, expect ~20 episodes downloaded the first day (free quota), the rest over the next two days unless upgraded. Sidecar filenames will be Futurama.s01eXX.pl.eng.srt next to each .mkv.