No description
Find a file
ventoz91 70c96d60e7 Add toggleable keybinds footer
'?' toggles a Textual Footer widget showing all active keybindings,
docked under the status bar (wrapped in a shared #bottom-bar container
so the two don't overlap - Textual's dock doesn't auto-stack multiple
docked siblings on the same edge).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 20:36:54 -06:00
ytui Add toggleable keybinds footer 2026-08-12 20:36:54 -06:00
.gitignore Initial commit: YouTube TUI (Textual) with OAuth login, playlists, subscriptions, library, and mpv playback 2026-08-11 23:12:21 -06:00
pyproject.toml Add pyproject.toml for pip install + a real ytui command 2026-08-12 20:25:42 -06:00
README.md Add toggleable keybinds footer 2026-08-12 20:36:54 -06:00
TODO.md Add playback control/shuffle, fix playlist cap and description-panel layout bug 2026-08-12 20:21:09 -06:00

ytui

A personal terminal YouTube client built with Textual. Search YouTube, browse your real playlists and subscriptions (via Google login), save videos to a local library, and hand off playback to mpv.

Loosely modeled on youtube-tui's layout, but with a more reliable backend: public browsing goes through yt-dlp directly instead of a public Invidious instance, and account data (playlists/subscriptions) uses real OAuth against the YouTube Data API instead of not being supported at all.

Features

  • Search — powered by yt-dlp, no API key or quota involved.
  • Playlists — log in with your Google account to see your actual playlists and drill into them.
  • Subscriptions — list of channels you're subscribed to; drill into one for its recent uploads.
  • Library — save videos locally (no login needed) for quick access later.
  • Play all from here — queue playback from any list (search results, a playlist, a channel, your library) starting at the highlighted video; mpv auto-advances through the rest natively.
  • View channel — jump from any video to that channel's recent uploads, whether or not you're subscribed.

Requirements

  • Python 3.11+
  • mpv — used for all playback
  • ffmpeg — used by yt-dlp/mpv to merge separate video/audio streams

Both mpv and ffmpeg are expected to already be on your PATH.

Setup

git clone https://git.ventoz.ca/trevor/ytui.git
cd ytui
python3 -m venv .venv
.venv/bin/pip install .

Logging in (optional, needed for Playlists/Subscriptions)

Search and channel browsing work with no setup. To see your own playlists and subscriptions, you need an OAuth client so the app can ask Google for permission on your behalf:

  1. In Google Cloud Console, open (or create) a project with the YouTube Data API v3 enabled.
  2. APIs & Services → OAuth consent screen:
    • User type: External (unless your account is on a Workspace org).
    • Fill in app name / support email / developer contact email — anything reasonable.
    • Under Test users, add your own Google account email. This is required — while the app is in "Testing" publishing status, only accounts listed here can complete login; everyone else gets a 403.
  3. APIs & Services → Credentials → Create Credentials → OAuth client ID, application type Desktop app. Download the JSON.
  4. Save it as ~/.config/ytui/client_secret.json.

The first time you press l in the app, it opens your browser for the Google consent screen and caches a token at ~/.config/ytui/token.json so you don't have to log in again.

Running

.venv/bin/ytui

Keybindings

Key Action
/ Focus the search box
Enter Submit search / select item / drill in
j / k or / Move down / up a list
l Log in (or refresh playlists/subscriptions if already logged in)
s Stop playback
? Toggle the keybinds bar at the bottom
Escape Go back a level, or return to search
q Quit

Search runs regardless of which tab is active and switches you to the Search tab. Every video list has an action menu (Enter again once a list item is selected) with: Play video, Play audio, Play all from here, Play all from here (audio), Shuffle play all, Shuffle play all (audio), View channel, Open in browser, Save to library — plus Remove from library when viewing your Library.

Project layout

ytui/
  app.py              Textual UI: tabs, panes, keybindings, wiring
  app.tcss             Styling
  player.py            mpv playback (single video or queued "play all")
  models.py             Shared Video/Channel/Playlist dataclasses
  sources/
    browse.py           Public search/metadata via yt-dlp (no login, no quota)
    account.py           OAuth + YouTube Data API v3 (playlists, subscriptions)
    library.py           Local JSON-backed saved-videos store

Architecture notes

  • Hybrid backend: anything public (search, a channel's uploads, a public playlist) goes through yt_dlp as a library, with no API key and no quota ceiling. Anything tied to your account (your playlists, your subscriptions) goes through OAuth + the official YouTube Data API v3, where call volume is low enough that the free quota is a non-issue.
  • Playback: player.py shells out to mpv --ytdl-format=... <url> [<url> ...]. Passing multiple URLs is what makes "Play all from here" work — mpv queues and auto-advances through them on its own, no custom playback-queue logic needed on our end.