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/.
323 lines
15 KiB
Markdown
323 lines
15 KiB
Markdown
# 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 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 <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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```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:
|
||
|
||
```
|
||
<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:
|
||
|
||
```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 <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`.
|