Show what you are watching in mpv as Discord Rich Presence. Add a free TMDb API key for official titles, posters, episode names, and episode stills.
- Displays Watching
<title>with play, pause, buffering, and idle states - Shows a speed-aware Discord progress bar
- Cleans common release tags from filenames before matching
- Supports common movie, TV, scene, and anime filename formats
- Displays episodes as
04 of 20: Chattywhen TMDb has the data - Uses movie posters, show posters, and episode stills from TMDb
- Reconnects automatically when Discord restarts
- Caches metadata across mpv sessions
- Optionally uses a local TMDb title index to reduce online searches
- Updates only when playback state changes; there is no constant presence polling
- mpv
- Discord desktop with Settings > Activity Privacy > Display current activity as a status message enabled
curlonPATHfor TMDb metadata and artwork- A free TMDb API key for TMDb features
LuaJIT is recommended and is required on Windows for background Discord disconnect detection and automatic local-index maintenance. Without a TMDb key, basic Rich Presence still works.
You do not need a Discord bot, OAuth flow, or install link.
Copy this structure into mpv's configuration directory:
scripts/
+-- discord-mpv-rpc/
|-- main.lua
|-- modules/
|-- db/
+-- tools/
script-opts/
+-- discord-mpv-rpc.conf
| OS | mpv configuration directory |
|---|---|
| Windows | %APPDATA%\mpv |
| Linux / macOS | ~/.config/mpv |
| Portable Windows | <mpv_dir>\portable_config |
Keep the complete modules/, db/, and tools/ folders beside main.lua.
Open script-opts/discord-mpv-rpc.conf and add your TMDb key:
tmdb_api_key=your_tmdb_key_hereThen start Discord, open a video in mpv, and the activity should appear automatically. Press D to toggle Rich Presence.
The included Discord Application ID is used when client_id is empty. To use your own application, create one in the Discord Developer Portal, copy its Application ID, and set:
client_id=your_application_idWith a custom application, optionally upload square Rich Presence assets named mpv, play, and pause, or change the corresponding asset keys in the configuration.
| Option | Default | Purpose |
|---|---|---|
client_id |
built in | Custom Discord Application ID; leave empty to use the default |
tmdb_api_key |
empty | Enables TMDb titles, episode data, links, and artwork |
tmdb_language |
en-US |
Language for TMDb searches and metadata |
tmdb_episode_lookup |
yes |
Looks up the exact parsed season and episode |
tmdb_local_index |
yes |
Enables the local TMDb index and automatic maintenance |
tmdb_index_mpv_path |
empty | Optional path to a LuaJIT-enabled mpv for the index worker |
tmdb_positive_cache_days |
60 |
Days before successful TMDb metadata is refreshed |
cache_path |
empty | Persistent metadata-cache path; empty stores it beside main.lua |
key_toggle |
D |
Toggles Rich Presence for the current session |
key_toggle_db |
Ctrl+d |
Toggles local-index lookup for the current session |
large_image |
mpv |
Fallback large-image asset key |
large_text |
mpv |
Fallback image hover text |
small_image_playing |
play |
Playing badge asset key; empty hides it |
small_image_paused |
pause |
Paused/buffering badge asset key; empty hides it |
small_image_idle |
mpv |
Idle badge asset key; empty hides it |
poster_fit |
contain |
contain letterboxes artwork through wsrv.nl; raw uses the TMDb URL directly |
enabled |
yes |
Enables Rich Presence at startup |
ignored_paths |
[] |
JSON array of files or directories excluded from all Rich Presence and metadata handling |
Restart mpv after editing the configuration file.
To exclude private media, test clips, or a complete directory tree, provide a JSON array of paths:
ignored_paths=["~~/watch-later/private","/mnt/media/home-videos","/mnt/media/test.mkv"]File entries match that exact file. Directory entries also match every file
below the directory. Absolute paths and mpv path prefixes such as ~~/ are
supported; relative paths are resolved against mpv's working directory.
Use forward slashes for Windows paths, for example C:/Media/Private, to avoid
JSON backslash escaping. Invalid JSON is ignored with a warning in mpv's log.
Ignored media clears any previous Discord activity and does not trigger
metadata lookup, TMDb requests, presence updates, or reconnection attempts.
The title is selected in this order:
- Official TMDb title
- File
metadata/title - Cleaned filename
- mpv
media-title
For TV episodes, the state line uses the TMDb episode title and season total when available:
Dragon Ball DAIMA
04 of 20: Chatty
If the total is unavailable, it shows 04: Chatty. If no TMDb episode title is found, a meaningful chapter title or the current playback state is used. Missing episodes are never guessed or remapped to another season.
While playing, Discord receives timestamps calculated from the current position, duration, and playback speed. Timestamps are removed while paused or buffering and recalculated after resuming or seeking.
Common formats are recognized automatically:
Movie.Name.2026.1080p.BluRay.mkv
Show.Name.S02E05.mkv
Show.Name.2x05.mkv
Show Name Season 2 Episode 5.mkv
[Judas] Dragon Ball Daima - S01E04v2.mkv
[SubsPlease] Show - 04 [1080p].mkv
The parser removes bracketed metadata and common trailing source, resolution, codec, audio, bit-depth, and release-group tags. It can also use a parent folder such as Show Name (2026) for title and year context.
Parsing is heuristic. For a bad match, check the cleaned-title log entry and simplify unusual filenames or folder names.
The bundled index is built from TMDb daily ID exports. It helps resolve exact movie and TV titles locally before falling back to the normal online TMDb search. A TMDb API key is still required to retrieve metadata and artwork.
When tmdb_local_index=yes, a detached mpv worker performs quick index checks at startup and hourly. Full checksums are limited to once per day unless corruption is detected. It downloads and rebuilds only when the index is:
- missing
- corrupted
- at least seven days old
A structurally valid active index remains available for lookups while an old snapshot is refreshed. Automatic maintenance does not start until a TMDb API key is configured.
The updater is pure Lua and uses mpv, LuaJIT, and the existing curl executable; no Python, database engine, or unzip tool is required. Playback, cached results, and online TMDb searches continue while maintenance runs. Completed indexes are picked up automatically. Downloaded exports and inactive index generations are removed after successful validation.
Set tmdb_local_index=no to disable both lookup and automatic maintenance. Ctrl+D toggles them for the current session without rewriting the configuration; it does not stop a worker already running.
Automatic builds can use several hundred MB of memory and require the script directory to be writable. Failed builds leave the active index unchanged and retry no more than once per hour.
To run the same validation/update manually from the installed script directory:
mpv --no-config --load-scripts=no --idle=yes --vo=null --ao=null --script=tools/update_tmdb_index.luaGenerated index files stay in db/tmdb/.
Metadata is cached in discord-mpv-rpc-posters.json beside main.lua in the script directory by default. Set cache_path to override it; mpv path prefixes such as ~~/ are supported. Successful metadata is refreshed periodically according to tmdb_positive_cache_days; missing results use shorter retry windows. Close mpv and delete the cache file when you intentionally want to retest matching from a clean cache.
TMDb requests are paced, coalesced, cached, cancelled when stale, and backed off after HTTP 429 responses. The local index does bounded disk lookups rather than loading the full database into memory during playback.
With poster_fit=contain, the TMDb image URL is sent to wsrv.nl to fit portrait artwork into Discord's square image area. Use poster_fit=raw to avoid that third-party image proxy; Discord may crop the image.
| Problem | Check |
|---|---|
| No Discord activity | Start Discord, enable Activity Privacy, and verify a custom client_id if used |
| No TMDb artwork or titles | Set tmdb_api_key, ensure curl is on PATH, and inspect mpv's logs |
| Wrong movie or show | Check the cleaned title and TMDb selected log entries; clear the cache when retesting |
| No episode title | TMDb may not contain that exact season/episode, or tmdb_episode_lookup may be disabled |
No of <total> text |
The season count is unavailable or smaller than the current episode number |
| Poster is cropped | Set poster_fit=contain |
| Do not want wsrv.nl | Set poster_fit=raw |
| Discord restarted after mpv | Reconnection is automatic; background detection on Windows requires LuaJIT |
| Local index does not build | Use LuaJIT-enabled mpv, verify curl, and make the script directory writable |
Run mpv from a terminal or enable verbose logging for more detail.
Run the pure-Lua checks with a standalone Lua interpreter:
lua tests/run.luaThe mpv suite runs those checks under mpv's real LuaJIT runtime, then tests script startup, headless playback events, scene/movie filename parsing, and the Rich Presence toggle binding:
tests/run-mpv-tests.sh --mpv /path/to/mpv --media /path/to/video.mp4 \
--database /path/to/db/tmdbFor a dynamically linked portable mpv, add --lib-dir /path/to/libraries.
The same paths can be supplied through MPV_BIN, MPV_TEST_MEDIA, and
MPV_LIBRARY_PATH. Tests disable TMDb networking and local-index maintenance,
use temporary symlinks instead of copying the video, and do not require Discord.
When ffmpeg and a test database are available, the suite also verifies a real
network stream and a read-only lookup against the existing TMDb index. Set
FFMPEG_BIN and MPV_TEST_DATABASE instead of the corresponding command-line
options if preferred.
Movie and TV metadata and artwork are provided by TMDb. The pure-Lua index updater includes LibDeflate under its original zlib license. Discord is a trademark of Discord Inc. This project is not affiliated with or endorsed by Discord or TMDb.

