The YouTube Queue for the Terminal.
ytq ("YouTube Queue") is an offline-first CLI for saving YouTube videos to a personal queue, opening the next or a random pick in your browser, and tracking what you watched over time. It supports queue and stack modes, multiple YouTube URL formats, optional metadata fetching via the YouTube Data API, and built-in stats so your backlog stays searchable, lightweight, and out of your tabs.
You need Rust 1.95 or newer. Development tracks the stable channel via rust-toolchain.toml, but 1.95 is the tested minimum and CI verifies it on every change. If you don't have Rust, get it from rustup.rs; existing rustup users can update with rustup update stable.
Clone the repo and install the binary to your global path:
# 1. Clone the repo
git clone https://github.com/jrzimerman/ytq
cd ytq
# 2. Install (Compiles release build & moves to ~/.cargo/bin)
cargo install --path .Note: Ensure ~/.cargo/bin is in your system $PATH.
ytq accepts the following YouTube URL formats:
| Format | Example |
|---|---|
| Standard watch URL | youtube.com/watch?v=VIDEO_ID |
| Short link | youtu.be/VIDEO_ID |
| Shorts | youtube.com/shorts/VIDEO_ID |
| Live streams | youtube.com/live/VIDEO_ID |
| Embed | youtube.com/embed/VIDEO_ID |
| Legacy v/ | youtube.com/v/VIDEO_ID |
| Legacy e/ | youtube.com/e/VIDEO_ID |
| Mobile | m.youtube.com/watch?v=VIDEO_ID |
| YouTube Music | music.youtube.com/watch?v=VIDEO_ID |
| Direct ID | VIDEO_ID (11 characters) |
Not supported: Channel URLs, playlist URLs, and search result URLs. These will display a helpful error message suggesting you provide a direct video link instead.
- Stash a video - Works with full URLs, short links, shorts, live streams, or just the video ID.
ytq add https://www.youtube.com/watch?v=dQw4w9WgXcQ
ytq add https://www.youtube.com/shorts/dQw4w9WgXcQ
ytq add dQw4w9WgXcQ- Watch the next video - Opens your default browser with the next video in queue.
ytq next- Feeling lucky? - Pop and watch a random video from the queue.
ytq random| Command | Shortcut | Aliases | Description |
|---|---|---|---|
ytq add <input> |
a |
Add video. Accepts URLs or IDs. | |
ytq next [target] |
n, p, w, o |
play, watch, open |
Watch & pop. Opens browser, logs event, removes from queue. |
ytq random |
r |
lucky |
Pop and watch a random video from the queue. |
ytq peek [n] |
k |
Look ahead. Show the next n videos (default: 1). | |
ytq list |
l |
ls |
List all. Shows the full queue. |
ytq remove <target> |
d |
rm, delete |
Delete. Removes item by ID or URL matching. |
ytq fetch [target] |
f |
Fetch video metadata from YouTube Data API v3. | |
ytq stats |
s |
Metrics. Shows current-year viewing statistics by default. Supports --wrapped, --all, --week, --month, --year, --from, --to. |
|
ytq config <key> <value> |
c |
Settings. Keys: mode, offline, youtube_api_key. |
|
ytq info |
i |
Debug. Prints the exact paths where your data is stored. |
Your preferences live in config.json. You can modify them via the CLI.
Switch to "Stack" Mode (LIFO) - Watch the most recently added video first.
ytq config mode stackSwitch back to "Queue" Mode (FIFO)
ytq config mode queueytq is offline by default - no network requests are made unless you explicitly enable online features.
Enable online features:
ytq config offline falseSet your YouTube Data API v3 key:
ytq config youtube_api_key YOUR_KEY_HEREOr use an environment variable (takes precedence over config):
export YOUTUBE_DATA_API_KEY=YOUR_KEY_HEREWhen online features are enabled, the fetch command retrieves video metadata (title, channel, duration, tags, etc.) from the YouTube Data API v3.
# Fetch metadata for all queue videos missing metadata
ytq fetch
# Fetch with a limit (useful for testing)
ytq fetch --limit 5
# Fetch for a specific video (force-refresh)
ytq fetch dQw4w9WgXcQ
# Fetch for multiple videos (comma-separated, force-refresh)
ytq fetch dQw4w9WgXcQ,jNQXAC9IVRw
# Fetch for all videos (queue + history)
ytq fetch --all
# Fetch for history videos only
ytq fetch --history
# Force refresh video categories
ytq fetch --refresh-categoriesMetadata is stored in the metadata table inside ytq.db, keeping reads indexed and writes fast. Video categories are cached in categories.json and only fetched on first run (or with --refresh-categories).
When metadata is available, list and peek show enriched output with video titles, channels, and durations:
4 videos in queue:
# ID Title Channel Duration Added
1 dQw4w9WgXcQ Never Gonna Give You Up (Officia... Rick Astley 3:34 2026-02-14 10:30
2 jNQXAC9IVRw Me at the zoo jawed 0:19 2026-02-13 09:15
3 abc12345678 (run `ytq fetch`) 2026-02-12 08:00
4 def12345678 (run `ytq fetch`) 2026-02-11 07:00
ytq tracks your queue behavior and viewing patterns. The stats command shows a summary of your activity:
# Current-year overview
ytq stats
# All-time overview
ytq stats --all
# Full "wrapped" deep dive with charts and leaderboards
ytq stats --wrappedTime filtering lets you scope stats to any period:
ytq stats --week # Last 7 days
ytq stats --month # Last 30 days
ytq stats --month 2026-01 # Specific month
ytq stats --year # Last 365 days
ytq stats --year 2025 # Specific year
ytq stats --from 2025-06-01 --to 2025-12-31 # Custom range
ytq stats --wrapped --year 2025 # Combine with --wrappedBasic stats (always available from the event log):
- Videos added, watched, skipped counts
- Completion rate and queue depth
- Average time in queue before watching
- Most active day of week
Wrapped stats (--wrapped flag adds):
- Monthly activity bar charts (added and watched)
- Time-of-day distribution (morning/afternoon/evening/night)
- Busiest day and longest watch streak
- Top channels and category breakdown with bar charts
- Top tags, skip rate, queue throughput
- Longest/shortest videos, fastest/slowest time-to-watch
When metadata is available (via ytq fetch --history), stats are enriched with total watch time, channel rankings, categories, tags, and video durations. Without metadata, core event-log stats still work — no network requests are ever made by stats.
ytq uses platform-specific paths for data storage. Run ytq info to see where your data lives.
| File | Purpose |
|---|---|
config.json |
User configuration (mode, offline, API key) |
ytq.db |
SQLite database holding the queue and metadata tables |
categories.json |
YouTube video category lookup table |
history/*.jsonl |
Event history logs (partitioned by month) |
Queue and metadata are read exclusively from ytq.db.
config.json can hold your API key, so it is created with owner-only permissions (0600) on Unix. Both config.json and categories.json are written atomically — a crash mid-write leaves the previous file intact rather than truncating it.
Two environment variables override the platform defaults. They are useful for sandboxing, for keeping separate queues, and for testing against throwaway data:
| Variable | Overrides |
|---|---|
YTQ_CONFIG_DIR |
Directory holding config.json |
YTQ_DATA_DIR |
Directory holding ytq.db, categories.json, and history/ |
# Run against a scratch queue without touching your real one
YTQ_DATA_DIR=/tmp/ytq-scratch YTQ_CONFIG_DIR=/tmp/ytq-scratch ytq listUnset or empty values fall back to the platform defaults. Run ytq info to confirm which paths are in effect.
Want to hack on ytq?
# Fast compile check
cargo check
# Build
cargo build
cargo build --release
# Format code
cargo fmt
cargo fmt --check
# Lint
cargo clippy -- -W clippy::all
# Match CI locally
cargo clippy -- -D warnings
# Run the test suite
cargo test
# List all tests
cargo test -- --list
# Run a single test by name fragment
cargo test valid_video_id_direct
cargo test basic_stats_counts
# Run tests in one module
cargo test youtube::tests
cargo test stats::tests
# Show test stdout
cargo test valid_video_id_direct -- --nocapture
# Run locally without installing
cargo run -- list
cargo run -- add https://www.youtube.com/watch?v=dQw4w9WgXcQCI runs the full test and Clippy suites on the latest stable Rust and checks formatting:
cargo test --locked --all-targets --all-features
cargo fmt --check
cargo clippy --locked --all-targets --all-features -- -D warningsBefore opening a PR, run:
cargo fmt
cargo clippy --locked --all-targets --all-features -- -D warnings
cargo test --locked --all-targets --all-featuresTo remove ytq and all associated data, follow these steps. Windows users may need to adjust paths.
- Remove the binary:
cargo uninstall ytq- Clear your data and history (run
ytq infoto confirm these paths first):
rm -rf ~/.local/share/ytq
rm -rf ~/.config/ytq