A macOS app for organizing your YouTube video library by topic. Like sorting your VHS collection, but with AI.
Download the latest packaged app from GitHub Releases.
- macOS 15+
- An Anthropic API key for topic generation and classification
- Optional: a YouTube API key for richer discovery refreshes
- Optional: Google OAuth setup if you want playlist saves, private playlist reads, or Watch Later sync
- Download the latest
Be.Kind.Rewind.zipfrom Releases. - Unzip it.
- Move
Be Kind, Rewind.appintoApplications. - Open the app.
On first launch, new installs with missing credentials will show a short setup screen that points you to the right Settings panels. If you already have keys configured, the app skips that screen.
In the app:
- Open
Settings - Go to
Accounts - Paste your Anthropic API key
- Click
Save Anthropic Key
The key is stored securely in your macOS Keychain.
This improves discovery refreshes and playlist verification.
In the app:
- Open
Settings - Go to
Accounts - Paste your YouTube Data API key
- Click
Save YouTube API Key
The key is stored securely in your macOS Keychain.
If you want:
Save to Watch LaterSave to Playlist- private playlist provenance
- browser fallback actions like
Not Interested
then do this:
- Create a Google OAuth desktop client.
- Download the client JSON.
- In the app, open
Settings. - In the
Accountspane, clickImport OAuth Client JSONβ¦. - Select the downloaded JSON file.
- Click
ReconnectorUpgrade Access. - In the
Advancedpane, useOpen Browser Sign-Inonce so the browser fallback profile is signed into YouTube too.
If you export Google Takeout / My Activity watch history, you can import it in:
Settings > Watch > Import Watch Historyβ¦
That suppresses already-watched videos from the Watch candidate list.
If you already have keys configured elsewhere, the app also reads:
- Anthropic:
~/.config/anthropic/api-keyANTHROPIC_API_KEY
- YouTube Data API:
~/.config/youtube/api-keyYOUTUBE_API_KEYGOOGLE_API_KEY
- Google OAuth client config:
~/.config/youtube/oauth-client.json
Takes a collection of YouTube videos (from one or more playlists) and organizes them into topic categories using Claude AI:
- Scan β Capture video titles and channels from a playlist
- Classify β Claude analyzes channels and titles to suggest ~15-25 topic categories
- Browse β Visual grid of video thumbnails grouped by topic with sticky section headers
- Discover β Generate per-topic watch candidates from creator archives, playlist overlap, and fallback discovery
- Refine β Split broad topics, merge similar ones, rename, or move individual videos
- Sync β Save to playlists, queue browser-backed actions, and keep local state optimistic and fast
- Two-step approach: discover topics from channels, then classify videos against the fixed list
- Haiku for fast initial classification (~$0.15 for 5K videos)
- Sonnet for topic splitting and refinement
- Sub-topic discovery within categories
- iPhoto-style adaptive thumbnail grid
- Sidebar with topic icons and colors
- Keyboard navigation: h/j/k/l, arrows, Page Up/Down, Home/End
- Hover-driven inspector updates
- Native context menus and menu bar actions
- Keyboard triage shortcuts for Watch / playlist management
- Per-topic
Watchmode with scraper-first discovery (no API quota for reads) - Real Settings window for API access, history import, and sync status
- Double-click to open on YouTube
- Adjustable thumbnail size
- Persistent thumbnail cache (~75MB for 5K videos, instant after first load)
- Persistent SQLite-backed state for playlists, candidates, seen-history, and sync queue
# Suggest topics from an inventory
video-tagger suggest --inventory inventory.json --topics 15
# Preview sub-topics within a category
video-tagger subtopics 2 --count 5
# Split a broad topic
video-tagger split 1 --into 4
# Merge similar topics
video-tagger merge 3 7
# Reclassify unassigned videos
video-tagger reclassify
# Resync playlist provenance for all known playlists
video-tagger verify-all-playlists --db /tmp/full-tagger-v2.db
# Push queued playlist-save actions to YouTube
video-tagger sync-pending --db /tmp/full-tagger-v2.db
# Open the persistent browser profile and sign in to YouTube for browser-backed sync
video-tagger browser-sync-login
# Import seen-history from a Google Takeout/My Activity export
video-tagger import-seen-history --db /tmp/full-tagger-v2.db --file /path/to/watch-history.htmlPlaylist provenance notes:
- playlist identities can be imported from a
youtube-cliplaylists.jsonartifact - playlist memberships are verified via the YouTube API using stored OAuth tokens
- rerun
video-tagger verify-all-playlists --db /tmp/full-tagger-v2.dbwhenever you want to refresh playlist membership data for the current library - rerun
video-tagger sync-pending --db /tmp/full-tagger-v2.dbto manually flush queued playlist-save actions; browser-only actions likeNot Interestedremain deferred until a browser executor is attached - use
video-tagger browser-sync-loginonce to sign the dedicated browser-sync Chrome profile into YouTube; browser sync failure artifacts are written underoutput/playwright/browser-sync/ - import historical watch history with
video-tagger import-seen-history --db /tmp/full-tagger-v2.db --file /path/to/export.html; the importer supports best-effort.json,.html,.htm, and.txtTakeout/My Activity exports and exactvideo_idmatches are excluded from watch-candidate results
Runtime notes:
./build-app.shis the supported packaging path; it signs the app and bootstraps the managed discovery Python environment- discovery uses a repo-managed venv under
.runtime/discovery-venvwithscrapetubeinstalled fromscripts/requirements-discovery.txt; all read operations (channel uploads, search, icons) use scraping as the primary path with YouTube API as fallback - browser-backed sync uses the dedicated Chrome profile at
~/.config/be-kind-rewind/playwright-profile
- macOS 15+
- Swift toolchain with SwiftPM
- Anthropic API key (stored in
~/.config/anthropic/api-keyor macOS Keychain) - Optional: YouTube API key in
~/.config/youtube/api-key - Optional: Google OAuth desktop client config at
~/.config/youtube/oauth-client.json - An inventory.json from yt-cli
# Build everything
swift build
# Build and package the app
./build-app.sh
open "Be Kind, Rewind.app"
# Run tests
swift testCommits also run local Swift checks automatically through the repo's pre-commit hook. If staged changes include *.swift, Package.swift, or build-app.sh, the hook runs swift test before the commit completes. Docs-only commits skip the check. To bypass it intentionally, use SKIP_LOCAL_CHECKS=1 git commit ....
Sources/
βββ TaggingKit/ # Shared storage, YouTube clients, sync, discovery fallback
βββ VideoTagger/ # CLI executable and workflow commands
βββ VideoOrganizer/ # SwiftUI/AppKit macOS app
βββ OrganizerStore # Main observable app state (facade)
βββ OrganizerStore+* # Discovery, sync, creator analytics, video actions, seen-history
βββ OrganizerViewModels # View models and enums
βββ GridSection* # Grid shaping and grouping logic
βββ CollectionGridView # NSViewRepresentable bridge and coordinator
βββ CollectionGridAppKit # AppKit container, cells, keyboard handling
βββ CollectionGridCellContent # SwiftUI cell and header content
βββ TopicSidebar # Topic/filter navigation
βββ AppSettingsView # API/history/sync settings
- Local-first: All changes happen instantly in SQLite. No YouTube mutations until explicit sync.
- Commit table: Queued mutations are collapsed to net effects before sync (AβBβC becomes AβC).
- Scraper-first Watch: channel uploads, video search, and channel icons use web scraping (no API quota). API is fallback only. See Watch Architecture.
- Tested across 3 test targets:
TaggingKitTests,VideoTaggerTests, andVideoOrganizerTests. - Current suite size: 147 tests across 25 suites covering storage, CLI workflows, organizer state, grid sectioning, OAuth, sync routing, discovery/archive behavior, thumbnail caching, and service models.
MIT
