Development¶
Setup¶
Prefer uv? Use uv pip install -e ".[dev,web]" (after uv venv), or uv run run.py to execute without activating the venv. See CLI Install for the full uv workflow.
Project Structure¶
├── run.py # CLI entrypoint (main playlist sync)
├── run_tags.py # CLI entrypoint (tag playlist sync)
├── src/ # Core sync engine
│ ├── config.py # Settings from environment variables
│ ├── context.py # RuntimeContext (shared dependencies)
│ ├── main.py # Facade for the workflow entry points
│ ├── webhook.py # Webhook notifications for sync events
│ ├── cache/ # Cache layer (search, playlist, tags)
│ ├── history/ # Optional SQLite history database for metrics and analytics
│ ├── lastfm/ # Last.fm API client and scrobble fetching
│ ├── observability/ # Observability helpers: failure logs, run logs, history DB recording, webhooks
│ ├── playlist/ # Playlist sync, diffing, weekly snapshots
│ ├── recency/ # Recency weighting algorithm
│ ├── search/ # YouTube Music search, scoring, and matching
│ ├── tags/ # Tag resolution, filtering, and tag playlist sync
│ ├── workflows/ # Workflow entry points
│ └── ytm/ # YouTube Music API wrapper
├── web/ # Web dashboard for Last.fm → YTM sync
│ ├── app.py # Flask web dashboard application
│ ├── run_log.py # Run log utilities for saving last run's mappings
│ ├── i18n/ # Internationalization helpers - Babel extractors and translation utilities
│ ├── routes/ # Web dashboard route blueprints
│ ├── services/ # Web dashboard services
│ ├── static/ # CSS, JS, icons
│ ├── templates/ # Jinja2 HTML templates
│ └── translations/ # i18n catalogs
├── cache/ # Runtime caches (gitignored)
├── config/ # User config files (overrides, custom playlists)
├── devops/ # Docker setup (Dockerfile, compose, entrypoint)
├── docs/ # This documentation (MkDocs)
└── runtime/
This tree is auto-generated
The structure above is produced by scripts/gen_structure.py and refreshed automatically by ./precommit.sh - don't edit it by hand (changes between the STRUCTURE TREE markers are overwritten). It walks the repo and pulls each entry's description from the first line of that module/package docstring, so the way to update a comment is to edit the docstring. Non-Python directories (e.g. templates/, static/) get their blurb from a small DIR_DESCRIPTIONS map in the script. Run python scripts/gen_structure.py to regenerate, or --check to fail if it's stale.
Testing¶
Unit tests are fast, offline, and need no browser:
Frontend e2e tests drive the dashboard with Playwright. The Playwright Python package lives in the web-docs extra, so install it (plus web) and a browser first:
pip install -e ".[dev,web,web-docs]"
python -m playwright install chromium
.venv/bin/python -m pytest tests/frontend
Both layers run automatically in CI and via ./precommit.sh. See the Testing docs for the full layout, coverage policy, and conventions for writing new tests.
Credits¶
- ytmusicapi - YouTube Music API wrapper
- Thanks to the Last.fm and YouTube Music communities.
License¶
This project is licensed under the MIT License - see the LICENSE file for details.