ARRFLIX/docs/03-subtitles.md

324 lines
15 KiB
Markdown
Raw Normal View 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:
```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=*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:
```
<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`.