legacy-arrflix/playbooks/subtitles/STYLE.md
s8n 24a9497e7d playbooks/ rename + import-media v1.0 + lilo&stitch run
processes/ -> playbooks/ (git mv preserves history; updated cross-refs
in ROADMAP, README, subtitles playbook + scripts).

playbooks/import-media/README.md v1.0 — 7-step import workflow:
  stage on onyx -> rsync to nullstone -> chmod -> verify scan ->
  Items/Counts bump -> optional subtitle pass -> run-log
Cross-references docs/05/07/08, ADMIN-GUIDE, README. Mirrors the
existing subtitles playbook structure (CHANGELOG + runs/_template).

CHANGELOG v1.0 lists known gaps (bin/cleanup-import.sh and
bin/normalize.py still doc-only, ROADMAP M6).

First run logged: playbooks/import-media/runs/lilo-stitch-2002.md.
Lilo & Stitch (2002) imported to /home/user/media/movies/, item
c2f4aff133c1b9631500fadf293b0b2f, TMDb 11544, MovieCount 3 -> 4.
LibraryMonitor didn't auto-fire — needed manual /Library/Refresh;
playbook updated to make this an unconditional step.

Source: 1080p BluRay HEVC 10-bit / EAC3 5.1 / 2x PGS embedded subs.
Per quality bar (README.md:41) — passes.
2026-05-10 02:29:57 +01:00

3.3 KiB

Subtitle USER-G style — ARRFLIX

The bar every fetch should hit. If a recipe step would violate any of these, stop and ask before proceeding.

What lands on disk

  • Exactly one English subtitle file per episode.
  • Filename: <videobasename>.eng.srt — no language-region tags (en-US), no flag stack on regular subs (no .sdh, no .forced, no .cc unless there genuinely is no plain-English option).
  • Format: .srt (SubRip text). Skip .ass, .ssa, .vtt, .sup, .idx unless the source has nothing else; convert with ffmpeg -map 0:s:0 -c:s srt in that case.
  • Encoding: UTF-8. Re-encode with iconv if a sidecar comes back as cp1252 / windows-1250.

What gets picked

In order:

  1. English language — eng / en. Never auto-pick en-US/en-GB variants over plain en; treat them equivalent for matching.
  2. No SDH / Hearing Impaired — drop any sub flagged hearing_impaired, sdh, cc. Only fall back to SDH if no plain-English option exists.
  3. No machine / AI translation — drop machine_translated, ai_translated. Hand-authored subs only.
  4. No forced subtitles — drop foreign_parts_only / Forced unless the episode has English audio with foreign-language scenes that need translation (rare for US shows).
  5. Frame-rate match — prefer entries whose declared fps matches the source video (typically 23.976 for our masters). Treat 0.0 as unknown and fall through to step 6.
  6. Highest download count within the surviving candidates — proxies for "the version everyone agreed was best."

After fetch, eyeball-verify one sample episode per show plays in sync (±1 s on a known dialogue line) before declaring the show done.

What doesn't ship

  • Multiple language tracks per episode (no German/French alternatives — English-only library).
  • Director's commentary, behind-the-scenes, song-only subs.
  • Subs that cover only a partial runtime (the partial-cover heuristic isn't scripted yet; spot-check duration vs episode runtime if a srt looks short).
  • "All-episodes-in-one" mega-packs treated as a single episode's sidecar.

How the UI presents subs

The detail-page subtitle dropdown is shimmed via web-overrides/index.html (markers SUB-LABEL-SHIM-BEGIN/END). Stock Jellyfin shows e.g. English - SUBRIP - External - Default; the shim collapses to English, with (Forced) / (SDH) / (Hearing Impaired) suffixes only when those flags actually apply. Default is dropped — it's redundant when there's only one stream per language.

Revert: bin/revert-sub-label-shim.sh.

Why these rules

  • Boutique-release-group quality bar from README.md: "every show and film is the best version I could put together."
  • One-language library = one stream per ep = no need to expose codec or source in UI.
  • SDH/CC adds [door slams], [music] etc. — distracting on first watch and not what someone reaches for unless they specifically need it.
  • Machine / AI translations are inconsistent and often wrong on slang or show-specific terms (esp. animated comedies).
  • Frame-rate-matched subs sync without manual offset on first try; mismatch generally still works on NTSC (29.97 vs 23.976 are the same elapsed time) but hash-match or fps-match removes that gamble.