medias
A TV media player that runs directly on DRM/KMS, without X11 or Wayland. libmpv decodes video into one KMS plane. A Vulkan-rendered UI sits on a second plane, and both are composited in one atomic page flip. Input comes from an HDMI-CEC remote, evdev keyboards and remotes, or the terminal. Targets are the Raspberry Pi and desktop GPUs (e.g. AMD RX 6800); see Hardware.
Features
- Local library: video and music folders with thumbnails (a frame at 15%, embedded or folder cover art). Per-file resume; search-as-you-type filter with an on-screen keyboard.
- UPnP/DLNA: discovers media servers via SSDP (e.g. Fritz!Box, minidlna) and browses their ContentDirectory. Album art is shown; items play from their HTTP resources.
- Folder playback: at the end of a file the next one in its folder plays (on by default, togglable). Next/Previous-track keys skip.
- YouTube: search, trending, subscriptions, playlists, liked videos (via YouTube Data API v3 sign-in), and "Like your watched" (videos related to watched ones). Streams play directly from InnerTube, with yt-dlp as fallback. The original audio track plays first; dubs in your languages are offered as named tracks.
- Views: text list, thumbnail list, or a wide thumbnail grid (Yellow/Info toggles).
- Black screen: Yellow/Info during playback cycles info shown → hidden → black screen. Audio keeps playing, which suits music on OLED TVs.
Requirements
- Linux with a KMS driver and an EGL/GLES driver (video).
- Optional: a Vulkan driver with dmabuf import (e.g. Mesa RADV or V3DV, or NVIDIA)
for the UI. Without one (e.g. Mali Midgard on RK3399) a CPU rasterizer draws
the UI;
MEDIAS_UI=cpuforces it. libmpv(packagempvorlibmpv-dev).libgbm,libEGLand the Vulkan loader are loaded or linked from the system.- Access to
/dev/dri/card*,/dev/dri/renderD*,/dev/input/event*, and to/dev/cec*for HDMI-CEC (groupsvideo,render,input). - No desktop or display manager holding the display (DRM master); otherwise
startup fails with
acquire DRM master ... Permission denied.sudo fuser -v /dev/dri/card*shows the holder. Stop it withsudo systemctl stop display-manager, or boot to a console withsudo systemctl set-default multi-user.target. - Optional:
yt-dlpfor the playback fallback and anonymous YouTube search.
Hardware
Video always renders through mpv's OpenGL path; GPU fill rate limits playback more than decoding does. Measurements: PERFORMANCE.md.
| Status | Hardware | Notes |
|---|---|---|
| Tested | Raspberry Pi 5 | Vulkan UI (V3DV). HEVC hwdec; H.264 1080p25 software. |
| Tested | Desktop: AMD (RADV), NVIDIA | Vulkan UI. |
| Expected | Raspberry Pi 4 | Vulkan UI (V3DV); H.264 and HEVC hwdec. Untested. |
| Expected | RK3588/RK3588S (e.g. Radxa Rock 5C) | Mali-G610: PanVK Vulkan, Panthor kernel driver (recent mainline). 4x A76 decode 1080p in software. Untested. Not RK3582 (Rock 5C Lite): its GPU is disabled. |
| Best effort | Allwinner H618 (Orange Pi Zero3) | Mali-G31, no Vulkan: CPU UI. 1080p24 H.264 needs profile=fast (MEDIAS_MPV_CONF); 30 ms of a 41.7 ms frame. Set MEDIAS_AUDIO_DEVICE (e.g. alsa/plughw:CARD=HDMI,DEV=0). |
| Best effort | RK3399 (e.g. Asus Tinker Edge R) | Mali-T860, no Vulkan: CPU UI. Too slow on vendor kernels; mainline untested. |
For a new 1080p box, buy a Raspberry Pi 4 or 5, or an RK3588 board.
Install
# with the "For you" feed (YouTube's TV OAuth client, see below):
Release builds for Linux x86_64 and aarch64 (tar.gz and Debian 13 trixie
.deb, in dist/) need cargo-make, cargo-zigbuild, zig, binutils, and the
host's libmpv and libgbm (their symbols form the link stubs):
Apt repository: cargo make repo-key creates the signing key once (ed25519,
passphrase). cargo make repo adds dist/*.deb to dist/debian/pool and
writes the signed indexes, medias.asc and medias.sources (key embedded).
The pool keeps every version; upload without --delete:
&&
On a Debian 13 client as root:
&&
Service
medias@.service runs medias on tty1 as the instance's user, with a logind
session (PipeWire, device access) and groups video render input audio.
Options go in /etc/default/medias (MEDIAS_ARGS, MEDIAS_* variables).
# Edit /etc/default/medias to configure.
# Replace USER with the user you want medias (and e.g. pipewire) to run as.
The .deb installs the unit to /usr/lib/systemd/system. From the tar.gz, copy
medias to /usr/bin, medias@.service to /etc/systemd/system and
medias.default to /etc/default/medias.
Run
The same command works from a Linux console, SSH or a terminal multiplexer.
scripts/medias-vt optionally starts it on a separate VT (see
scripts/README.md).
Files under ~/medias
| Path | Purpose |
|---|---|
Videos/, Music/ |
default library roots |
upnp.txt |
UPnP server description URLs outside multicast reach, one per line |
youtube.txt |
YouTube sources, Label | query per line (yt-dlp query or URL) |
youtube_oauth.json |
Data API sign-in: {"client_id", "client_secret", "api_key"?} |
.history |
resume positions and last-played dates |
.youtube_auth, .youtube_tv_auth |
stored sign-in sessions (mode 0600) |
.view, .autoplay |
list/grid view (default grid); play-next-in-folder on/off |
.cache/thumbs/ |
thumbnail cache |
YouTube sign-in
- Data API (subscriptions, playlists, liked videos): create a Google Cloud
OAuth client of type "TVs and Limited Input devices", enable YouTube Data
API v3, and put its ID and secret in
~/medias/youtube_oauth.json. "Sign in" then shows a code and a QR code. In the consent screen's "Testing" state, Google expires the refresh token after 7 days. - For you (build feature
youtube-tv): a second sign-in with YouTube's own TV client (as the Kodi YouTube plugin does). No configuration is needed. The feed loads up to 36 videos. The same sign-in adds "Watched" and "Playlists" (own and saved, up to 200 entries each).MEDIAS_YT_SYNC=1adds videos played in medias to that account's YouTube watch history (off by default). - Without either, search uses yt-dlp anonymously.
Remote keys (summary)
| Key | Browse | Playback |
|---|---|---|
| Up/Down | move | volume |
| Left/Right | page (grid: move) | seek ±10 s |
| OK | open / play | playback menu |
| Back | up a level | back to the library (paused) |
| Rewind/Fast-forward, Channel ± | page | seek ±60 s |
| Forward/Backward (Next/Previous track) | page | next/previous file in folder |
| Red / Green | — | subtitles / audio track |
| Blue | on-screen keyboard | playback menu |
| Yellow | list/grid view | info → hidden → black screen |
License
GPL-2.0-or-later (LICENSE). The embedded Liberation Sans font is under the
SIL Open Font License 1.1 (assets/LiberationSans-OFL.txt).