A terminal UI for YouTube, Twitch, Kick and PeerTube, written in Rust.
██╗░░██╗██╗██████╗░██╗
██║░░██║██║██╔══██╗██║
╚██╗██╔╝██║██║░░██║██║
░████╔╝░██║██║░░██║██║
░╚██╔╝░░██║██████╔╝██║
░░╚═╝░░░╚═╝╚═════╝░╚═╝
YouTube
- ⚡ Fast metadata via the Innertube API (no yt-dlp subprocess for lists; yt-dlp remains as fallback)
- 🔥 Trending videos
- 🔍 Search with history
- 📡 Subscription feed (parallel fetch, sorted by upload time, instant cached startup with background refresh)
- 📋 Browse subscribed channels (Videos / Shorts / Streams / Playlists)
- ➕ Subscribe to channels directly from Explore Channels
- 📥 Import subscriptions from NewPipe JSON, OPML or YouTube Takeout CSV (Miscellaneous → Import Subscriptions)
- ⏭ Play-next queue (
Tabto queue from any list, YouTube → Queue to play) - ⏯ Watch progress: playback position is tracked via mpv IPC and resumed on the next watch
- 🚫 SponsorBlock: mark segments as mpv chapters, cut them from downloads (
SPONSORBLOCKinvidi.conf) - 🙈 Shorts hidden everywhere by default — lists, search, feed and the channel Shorts tab (
SHOW_SHORTS: truere-enables) - 🎯 Custom playlists
- 🕐 Recently watched
- 🔎 Channel search via Miscellaneous → Explore Channels
Twitch
- 🔍 Live stream search
- 💜 Subscription status in one fast batched API call (live/offline, viewers, uptime), with a
streamlinkfallback - 🔥 Top live streams (Twitch directory)
- 🗂 Browse categories → live streams per game, with box-art previews
- ➕ Follow / unfollow channels in-app (writes
twitch_subs) - 🎬 VOD browsing via fast GQL (Past Broadcasts / Highlights / Uploads / Premieres), with view counts, durations and thumbnails — for subscribed channels or any stream you find; resumes where you left off; download with audio/quality options
- 💬 Live chat viewer with real Twitch name colours and broadcaster/mod/vip/sub badges (auto-reconnects)
Kick
- 🔍 Live channel search
- 💚 Subscription status for followed channels, with live/offline, uptime and thumbnails
- 🔥 Top live streams (Kick directory)
- 🗂 Browse categories → live streams per category
- ➕ Follow / unfollow channels in-app (writes
kick_subs) - 🎬 VOD browsing with durations and thumbnails; download or open in browser
PeerTube
- 🌐 Pick your instance on first use (prefilled, saved to
peertube.conf, changeable any time from the menu) - 🔥 Trending and 🆕 Recently Added from your instance
- 🔍 Search across the whole fediverse via SepiaSearch
- 📡 Subscription feed fetched straight from each channel's own instance, so new uploads show up before they federate (parallel fetch, cached startup with background refresh)
- 🔎 Explore channels and subscribe/unsubscribe in-app (writes
peertube_subs) ▶️ Watch, download, save, queue and resume exactly like YouTube videos
General
- Inline thumbnail previews (kitty / Ghostty graphics protocol, or iTerm2 / WezTerm)
- Inline video playback via
mpv/yt-dlp - Download support (video + audio-only, single + batch)
- Save/unsave videos to a local watchlist
- Fuzzy filter bar on all list screens (
/, vim-style) - Help overlay on
? - Update check on launch, with a prompt to install the latest release (
CHECK_UPDATES: falsedisables)
yt-dlp— 2026.08.19 or newer (see Playback and downloads)mpvstreamlink— Twitch and Kick streamswl-clipboard,xcliporxsel— copying URLs withy(optional)- A graphics-capable terminal for thumbnail previews: Kitty, Ghostty, iTerm2, or WezTerm (optional)
curl -fsSL https://raw.githubusercontent.com/Fel-2/Vidi/main/install.sh | shInstalls the latest release to ~/.local/bin. Static musl builds for x86_64
and aarch64, checksum-verified. Set PREFIX=/usr/local/bin to change the
destination or VERSION=v0.2.0 to pin a release.
git clone https://codeberg.org/Fel/Vidi.git
cd Vidi/packaging
makepkg -sicargo build --releaseBinary is at target/release/vidi.
vidi --version prints the version; it is also shown under the logo on the
start screen and in the ? help overlay.
Config files are created automatically on first run:
| File | Purpose |
|---|---|
~/.config/vidi/vidi.conf |
Player, quality, result limits, PLATFORMS, WATCH_PROGRESS, SPONSORBLOCK, YTDLP_CLIENT, CHECK_UPDATES |
~/.config/vidi/subscriptions |
YouTube channel URLs (one per line) |
~/.config/vidi/twitch.conf |
Twitch player and quality settings |
~/.config/vidi/twitch_subs |
Twitch usernames (one per line) |
~/.config/vidi/kick.conf |
Kick player and quality settings |
~/.config/vidi/kick_subs |
Kick channel names (one per line) |
~/.config/vidi/peertube.conf |
PeerTube instance (INSTANCE, optional SEARCH_INSTANCE) |
~/.config/vidi/peertube_subs |
PeerTube channels as channel@instance (one per line) |
~/.config/vidi/custom_playlists.json |
Custom playlist URLs |
In subscriptions, each line is a channel URL, optionally followed by whitespace
and a display name (https://www.youtube.com/channel/UC… My Channel). Providing
the name inline lets the Channels view skip the per-channel yt-dlp lookup, so it
loads instantly. Lines without a name are resolved once and cached in
~/.cache/vidi/channel_names.json.
PLATFORMS in vidi.conf controls which platforms appear on the start screen.
It takes a comma-separated list of youtube, twitch, kick and peertube
(aliases yt and pt work, case and spaces are ignored):
PLATFORMS: youtube,kick
Omitting the key shows all four, and an unrecognised value falls back to all four rather than leaving you with an empty menu. Hiding a platform only removes its menu — its config, subscriptions and history are left untouched, so re-enabling restores everything.
peertube_subs stores each channel with its home instance (blender_studio@video.blender.org);
channel URLs work too and are normalised on read. Because the handle carries the origin,
the feed queries each channel's own instance directly instead of waiting for federation.
To annotate an existing URL-only file in place, run:
scripts/name-subscriptions.pyIt reuses the cache where possible and only calls yt-dlp (4 at a time) for
names it doesn't already know. The original is backed up to subscriptions.bak.
vidi hands the video URL to mpv, which asks yt-dlp to work out the actual
stream URLs. That means playback depends on what yt-dlp decides to ask YouTube
for — and yt-dlp's own defaults change from release to release without warning,
so a new default can break playback for everyone running vidi.
To keep that decision ours, vidi pins the YouTube player clients it asks for:
YTDLP_CLIENT: visionos,web
visionos leads because it is the one client that needs neither a PO token nor
a JavaScript runtime — no cookies, no browser, nothing to keep in sync. web is
a fallback. vidi sets this explicitly on every yt-dlp invocation it makes, so it
takes precedence over both yt-dlp's defaults and any ~/.config/yt-dlp/config
you have. Override YTDLP_CLIENT if YouTube starts refusing the defaults; the
extractor argument accepts the usual yt-dlp client names.
This needs yt-dlp 2026.08.19 or newer, the first release that knows the
visionos client; older builds ignore clients they don't recognise and fall
back to their own defaults (vidi warns at startup when it sees that).
Downloads pin the same clients, so they don't drift either.
| Key | Action |
|---|---|
↑ / k |
Move up |
↓ / j |
Move down |
PgUp / PgDn |
Page up / down |
Enter |
Select |
Esc |
Back |
q |
Quit |
Tab |
Add video to queue (in Queue: remove) |
? |
Help overlay |
/ |
Fuzzy filter list (vim-style prompt: Enter keeps the filter, Esc clears it) |
Ctrl-W / Ctrl-U |
In the filter prompt: delete word / clear line |
n / N |
Jump to next / previous match of the last filter text |
g / G |
First / last row |
Ctrl-U / Ctrl-D |
Half page up / down |
Backspace |
Clear filter |
p / d |
Play / download the selected item |
y / s |
Copy URL to clipboard / toggle saved |
| Mouse wheel | Scroll lists |
Single-key overrides can be set in vidi.conf (KEY_UP, KEY_DOWN, KEY_SELECT,
KEY_BACK, KEY_QUIT, KEY_PAGE_UP, KEY_PAGE_DOWN). Arrow and vim keys always work.
Kick serves recorded videos as a live-style HLS playlist, so mpv sees an
unseekable stream: playback begins near the live edge and the position cannot be
moved. WATCH_PROGRESS does not apply to Kick VODs for the same reason — the
reported length is only the buffered window. Use Download to keep a copy you
can seek through locally.
Now and then a launch fails like this:
[ffmpeg] https: HTTP error 403 Forbidden
Failed to open https://rr2---sn-....googlevideo.com/videoplayback?...
No video or audio streams selected.
vidi retries this for you, so it usually resolves itself within a second or two. You do not need to press anything.
This is a transient rejection by YouTube's media CDN, not a problem with vidi,
yt-dlp or your setup. Because bestvideo+bestaudio streams the video and audio
tracks from two separate URLs, one of them being refused is enough to abort
playback. The refused URL is still valid: requesting it again seconds later
succeeds. Measured on a sample of 60 launches, roughly 15% failed this way, in
bursts.
vidi retries non-destructively: once with the same client (fresh URLs, same
quality), then once on its fallback client, which may be lower quality but plays.
If a video still won't start, the pinned client is the likely cause — see
Playback and downloads for YTDLP_CLIENT.
Retrying is safe here — the 403 comes from the media servers, not from YouTube's API, so it is not a rate limit or a bot check and does not count against you.
One exception: vidi deliberately does not retry "Sign in to confirm you're not a bot", "Video unavailable", or geo/membership refusals. Those are permanent for that request, and hammering the bot check can get your IP flagged for real. If you see one of those, the video itself is the problem.
MIT — see LICENSE.