# 03 — Subtitles on Jellyfin (arrflix.s8n.ru) Last updated: 2026-05-08 Server: Jellyfin 10.10.3 (X64) on nullstone, container `jellyfin` URL: 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 s01–s07 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 1–5; 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: ```bash 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 . 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: ```bash TOKEN=*redacted* 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: ```json { "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`: ```json { "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`) ```bash 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: ```bash 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: ``` .[.flag]. ``` 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: ```bash 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`: ```bash # 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): ```bash 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 ** | | 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`.