Harmony is a desktop music player built with:
- Python
- PySide6 (Qt6 GUI)
- SQLite
- mutagen (audio metadata)
The project prioritizes:
- modular architecture
- thread safety
- responsive UI
- extensibility for cloud sources
Install dependencies:
uv syncRun application:
uv run python main.pyRun tests:
uv run pytest tests/GitHub Actions use release.sh to build packages.
Database:
./Harmony.db
Tests:
tests/
The project follows a clean layered architecture with dependency inversion:
app/ → Application bootstrap and dependency injection
domain/ → Pure domain models (no dependencies)
repositories/ → Data access abstraction layer
services/ → Business logic layer
infrastructure/→ Technical implementations
ui/ → PySide6 user interface
system/ → Application-wide components
UI → Services → Repositories → Infrastructure
↘ Domain ↗
- UI only depends on Services and Domain
- Services depend on Repositories and Domain
- Repositories depend on Infrastructure and Domain
- Domain has no dependencies (pure dataclasses)
- Infrastructure implements technical details
AI agents should maintain this separation.
Never mix UI logic, database logic, and playback logic.
CRITICAL: These rules are enforced to maintain architectural integrity.
✗ FORBIDDEN: Direct database access from anywhere except DatabaseManager
✓ ALLOWED: Only infrastructure/database/DatabaseManager may access SQLite directly
✗ FORBIDDEN: Direct DatabaseManager access from UI or services ✓ ALLOWED: Only repositories may access DatabaseManager
✗ FORBIDDEN: Direct repository access from UI ✓ ALLOWED: Only services may access repositories
✗ FORBIDDEN: UI code may NOT access:
- Database (sqlite3, direct queries)
- DatabaseManager
- Repositories
- Infrastructure components
✓ ALLOWED: UI code may ONLY access:
- Services (through Bootstrap/injection)
- Domain models
- EventBus
- System components (ConfigManager, etc.)
When checking for violations, grep for:
# UI importing database
grep -r "sqlite3\|DatabaseManager" ui/
# UI importing repositories
grep -r "from repositories" ui/
# Services importing database
grep -r "sqlite3\|DatabaseManager" services/
# Non-repository importing DatabaseManager
grep -r "from infrastructure.database" --exclude="repositories/"Application bootstrap and dependency injection.
Important components:
Application- Application singletonBootstrap- Dependency injection container
Pure domain models with no external dependencies.
Important components:
Track,PlaylistItem- Music track entitiesPlaylist- Playlist entityCloudFile,CloudAccount- Cloud storage entitiesPlayMode,PlaybackState- Playback enumerations
Key rule:
Domain models must never import from other modules.
Data access abstraction layer.
Important components:
TrackRepository- Track data accessPlaylistRepository- Playlist data accessCloudRepository- Cloud account/file data accessQueueRepository- Play queue persistence
Key rule:
Repositories abstract database operations from business logic.
Business logic layer organized by domain.
Important subdirectories:
playback/- PlaybackService, QueueServicelibrary/- LibraryServicelyrics/- LyricsService, LyricsLoadermetadata/- MetadataService, CoverServicecloud/- QuarkService, CloudDownloadServiceai/- AiMetadataService, AcoustidService
Rules:
- services should avoid UI logic
- services should return clean data structures
Technical implementations.
Important components:
audio/- PlayerEngine + pluggable backends (mpv/ Qt Multimedia)database/- SqliteManagernetwork/- HttpClientcache/- FileCache
Contains all PySide6 UI components.
Structure:
windows/- MainWindow, MiniPlayerviews/- LibraryView, PlaylistView, QueueView, CloudViewwidgets/- PlayerControls, LyricsWidget, dialogs
UI should communicate through signals and the EventBus.
Avoid direct coupling between widgets and services.
Application-wide components.
Important components:
ConfigManager- Configuration managementEventBus- Global event busi18n- Internationalizationhotkeys- Global hotkeys
The project uses a global singleton EventBus for decoupled communication.
Typical usage:
bus = EventBus.instance()
bus.track_changed.connect(handler)Typical events:
- track_changed
- playback_state_changed
- position_changed
- download_progress
- lyrics_loaded
- tracks_added
UI components should subscribe to events instead of tightly coupling to services.
Cloud files are integrated through PlaylistItem.
Typical flow:
- User selects cloud file
- PlaybackService creates PlaylistItem(needs_download=True)
- CloudDownloadService downloads file
- On completion playback begins
Cloud downloads run in background threads.
Track search uses:
SQLite FTS5
Indexed fields:
- title
- artist
- album
Fallback search:
LIKE queries
Playback queue is persisted in the database.
Managed by:
QueueService.save()
QueueService.restore()
The queue supports:
- local tracks
- cloud tracks
Queue is restored on startup.
Important rules:
- database uses thread‑local connections
- downloads run in background threads
- lyrics loading runs in QThread
- UI thread must remain responsive
Communication between threads must use Qt signals.
Preferred patterns:
- dataclasses for models
- service classes for external logic
- signal‑driven UI updates
- centralized state management
- minimal UI‑service coupling
- dependency injection through Bootstrap
All tests are located in:
tests/
Use:
pytest
AI agents must ensure tests pass before completing tasks.
When new features are added, update:
README.md
When adding features or fixing bugs:
- update or add tests
- ensure:
pytest tests/
passes.
When major architecture or workflow changes occur, update:
CLAUDE.md
so future AI agents understand the project.
Save new documentation Markdown files to folder docs/
When modifying the project:
- Preserve layered architecture
- Maintain thread safety
- Keep UI responsive
- Avoid tight coupling
- Write tests for new behavior