RetroFed Forum
Your home away from modern life.
Arch Linux mpv Auto Subtitles agent skill
Post Reply
AuthorMessage
♠️ Critical Nexus ✊🏿 reddthat.com PumpkinDrama@reddthat.com
Joined: 2023-09-23
Quote

mpv Auto Subtitles

Date: 2026-09-12
Host: cachyos-B450 (local)
Config lives in: ~/.config/mpv → symlink to dotfiles/config/mpv/
Verified with: The Worst Witch S01E01 (24-min episode, 195 MB)


Summary

mpv gets subtitles in three layers, in this order. Each layer only runs when the
previous one found nothing:

# Layer What runs Trigger
1 Local files mpv itself on load
2 Download autosub.lua → subliminal on load, if no sub in the wanted language
3 Local AI whispersubs.lua → whisper-cli (whisper.cpp) manual: Ctrl+.

slang=en decides what counts as "already have it".


Layer 1 — local subtitle files

mpv.conf:

slang=en                            # preferred subtitle language
sub-auto=fuzzy                      # don't require an exact filename match
sub-file-paths=sub:subtitles:subs   # also look in these subfolders

Covers files like Video.mkv + Video.en.srt, including the .en.srt files that
subliminal writes next to the video.

Layer 2 — automatic download (subliminal)

  • Script: ~/.config/mpv/scripts/autosub.lua (davidde/mpv-autosub)
  • Subliminal: 2.7.1 installed via pipx → ~/.local/bin/subliminal (not an Arch
    repo package; pipx keeps it out of the system Python)
  • Config: ~/.config/subliminal/subliminal.toml → symlink to
    dotfiles/config/subliminal/subliminal.toml

Behaviour:

  • languages: en first, then es (first language with a hit wins; the script does
    not download both)
  • hotkeys: b = English, n = Spanish
  • skipped by design: files under 15 minutes, audio files, network streams
  • skipped when a subtitle in that language is already present
  • -d <video dir> + -e utf-8: the .srt lands next to the video, UTF-8
  • sub-auto=fuzzy + rescan_external_files make it load immediately, no restart

Provider reality (measured 2026-09-12, subliminal 2.7.x)

Provider Result
opensubtitles (opensubtitles.org) works anonymously, movie + TV, 1–2 s per lookup
opensubtitlescom (opensubtitles.com) finds candidates but never downloaded — needs a free account (≈5 downloads/day)
tvsubtitles HTTP 403 for scripted requests
podnapisi, addic7ed, bsplayer, subtitulamos, subtis, gestdown, napiprojekt returned nothing

Config therefore pins the one working provider and drops the broken refiner:

[download]
provider = ["opensubtitles"]
ignore_refiner = ["omdb"]   # subliminal's bundled OMDb key is expired → 401 on every lookup

A provider login (if ever needed) goes under [provider.<name>]:

[provider.opensubtitlescom]
username = "..."
password = "..."

Subliminal measure-check for new providers:

subliminal --debug download -p <provider> -l en -f -d /tmp/subtest <video>

--debug is a global option (before download), and -R omdb after it skips the
OMDb refiner. Search hits are not downloads — check that a .srt actually appeared.

Layer 3 — local AI subtitles (whisper.cpp)

  • Engine: whisper-cpp from the CachyOS repo → /usr/bin/whisper-cli
    (CPU-only: ggml backends in /usr/lib/ggml, no Vulkan/ROCm build)
  • Models: ~/.local/share/whisper.cpp/models/
    • ggml-small.en.bin — 466 MB, default (better accuracy)
    • ggml-base.en.bin — 142 MB, faster
  • Script: ~/.config/mpv/scripts/whispersubs.lua (GhostNaN/whisper-subs),
    WHISPER_CMD = whisper-cli -m <small.en path> --threads 8 --language en --no-prints
  • Ctrl+. toggles it; 15 s chunks ahead of playback; writes
    <video>.srt next to the video when it reaches the end

Throughput (Ryzen 7 5700G, 8 threads, 120 s of real dialogue)

Model Threads Time for 120 s audio Realtime factor
base.en 8 7 s ~17×
small.en 8 16 s ~7.5×
small.en 4 24 s ~5×

Full 24-min episode end-to-end: 395 cues, 00:00:00 → 00:23:45, 300 s wall clock
(the script reloads the model per 15 s chunk, hence ~5× rather than the raw 7.5×).


Files

Path Role
~/.config/mpv/mpv.conf slang, sub-auto=fuzzy, sub-file-paths
~/.config/mpv/scripts/autosub.lua download on load (subliminal path + languages)
~/.config/mpv/scripts/whispersubs.lua Ctrl+. live Whisper subtitles
~/.config/subliminal/subliminal.toml providers, ignore_refiner, logins
~/.local/bin/subliminal pipx venv entry point (pipx upgrade subliminal)
~/.local/share/whisper.cpp/models/ ggml Whisper models
/tmp/mpv_whisper_tmp_<pid>.* whisper-subs scratch files (auto-cleaned)

All of ~/.config/mpv and ~/.config/subliminal are symlinks into the dotfiles repo,
so the setup is version-controlled. The CachyOS post-install script installs it:
step 4b (whisper-cpp), step 12 (subliminal via pipx), step 14b (small.en model).

Verification (headless, no display needed)

# download layer
mpv --vo=null --ao=null --length=15 "<video without subs>"   # expect: [autosub] … subtitles ready!

# whisper layer: load a video, press Ctrl+. over the IPC socket, then poll the scratch file
mpv --idle=yes --input-ipc-server=/tmp/mpvsock &
#   {"command":["loadfile","<video>"]}   then   {"command":["keypress","ctrl+."]}
ls -l /tmp/mpv_whisper_tmp_sub_*.srt

# engine speed
whisper-cli -m ~/.local/share/whisper.cpp/models/ggml-small.en.bin -f chunk.wav -t 8 -np -osrt -of out

get_property input-bindings does not list script-added bindings — only the
functional keypress test proves Ctrl+. is wired up.

Pitfalls

  • Only opensubtitles.org works without credentials. If it dies, open a free
    opensubtitles.com account and switch the provider list (config above).
  • Ctrl+. / b / n must be free in input.conf (they are, as of this date).
  • Whisper produces an empty cue file for silent stretches; ffmpeg then logs
    Invalid data found when processing input and that chunk is skipped — harmless.
  • Install lag: pacman -S whisper-cpp 404'd on a stale mirror until
    cachyos-rate-mirrors + pacman -Syy.
  • ignored-provider names change between subliminal releases; re-run the
    measure-check above before trusting a provider list copied from the internet.
  • Subliminal writes UTF-8 regardless of the source encoding (-e utf-8) so old
    sub-codepage tweaks in mpv are unnecessary.
0   Original
Post Reply
Back to top