Skip to content

Development

Setup

pip install -e ".[dev,web]"

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:

.venv/bin/python -m pytest --ignore=tests/frontend

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.