Afzan Khan
afzanlearns
Back to Projects
TypeScriptNode.jsSQLiteCLI

vault

Private

About the Project

A fully terminal-native music client for YouTube Music — search, stream, download, tag, and browse an entire music library without ever leaving the command line.

Overview

vault treats the terminal as a real music app rather than a script wrapper. It searches YouTube Music's catalogue, streams audio directly through mpv with no video and no browser involved, saves tracks locally as tagged .m4a files, and organizes a personal library by genre and freeform tags — all through either a menu-driven interactive TUI or direct scriptable CLI subcommands.

How It Works

  • Search pulls results from YouTube Music via musicstream-sdk (Innertube/YouTubei.js), rendered as a tabular picker with right-aligned durations and width-aware truncation
  • Stream spawns mpv --ytdl --no-video --no-terminal directly against a YouTube URL — playback is headless and non-blocking, with stdin set to raw mode so a single q or Ctrl+C kills the process cleanly and restores the terminal
  • Save downloads the best available audio via yt-dlp -f bestaudio, storing files under ~/Music/vault/{genre}/ while recording metadata in a local SQLite database
  • Tag attaches freeform labels to saved tracks in a separate table with a foreign key back to the tracks table, enabling filtered library browsing (vault library --tag "favourite")
  • Interactive mode wraps all of this in an @inquirer/prompts-driven menu — arrow-key navigation throughout, a persistent now-playing indicator in the header, and a screen that clears between actions instead of stacking scroll history

Design Details Worth Noting

  • Security-first process spawning — every child process call uses array-based arguments (execFileSync/spawn with shell: false), eliminating shell string concatenation and any command-injection surface entirely
  • Clean warning suppression — Node's ExperimentalWarning (from node:sqlite via musicstream-sdk) and DeprecationWarning (from shell-less spawning) are intercepted at the entry-point shim before any module code runs, so the CLI output stays clean without silencing warnings globally
  • Schema migration on rename — when the project renamed its backing service, the database schema migrated the primary identifier column (audius_idvideo_id) automatically on first run, with no manual intervention required for existing libraries

Tech Stack

Node.js (TypeScript, via tsx, no build step) · commander · @inquirer/prompts · musicstream-sdk · yt-dlp · better-sqlite3 · mpv · chalk · ora

Usage

# Interactive mode
npm start

# Direct CLI
vault search "Mac Miller"
vault save dQw4w9WgXcQ
vault play 42
vault tag 42 "chill"
vault library --genre "Hip-Hop"