docs: map existing codebase

This commit is contained in:
2026-02-26 16:58:25 -05:00
parent d01f5e5d14
commit 572b7fb664
7 changed files with 2526 additions and 0 deletions
+260
View File
@@ -0,0 +1,260 @@
# External Integrations
**Analysis Date:** 2026-02-26
## Wails Runtime Bridge (Go ↔ TypeScript)
**Primary Communication Mechanism: Events**
The Wails runtime provides a bidirectional event bus between Go and TypeScript. Event names are defined as string constants that must match exactly between both sides:
- Go: `backend/events/events.go` - Centralized event name constants
- TypeScript: `frontend/src/events.ts` - Mirrored constants
**Event Categories:**
| Category | Direction | Events |
|---|---|---|
| Playback | Backend → Frontend | `PlaybackStateChanged`, `PlaybackFinished`, `TrackChanged`, `SeekFailed`, `VolumeChanged` |
| Queue | Backend → Frontend | `QueueChanged`, `QueueIndexChanged`, `QueueModeChanged`, `QueueTracksModified` |
| Config | Backend → Frontend | `LibraryConfigChanged`, `ThemeConfigChanged`, `TrackListConfigChanged`, `FavoritesConfigChanged` |
| Playlist | Backend → Frontend | `PlaylistCreated`, `PlaylistDeleted`, `PlaylistRenamed`, `PlaylistTracksChanged`, `PlaylistsRestored`, `DefaultPlaylistChanged` |
| Library | Backend → Frontend | `LibraryScanStarted`, `LibraryScanComplete` |
**Go event emission pattern:**
```go
runtime.EventsEmit(p.ctx, events.TrackChanged, trackInfo)
runtime.EventsOn(l.ctx, events.LibraryConfigChanged, func(data ...any) { ... })
```
**TypeScript event subscription pattern:**
```typescript
EventsOn(Events.TrackChanged, (trackInfo: TrackInfo | null) => { ... });
```
**Wails Bindings (Direct Function Calls):**
Go structs listed in `FEBindings` in `backend/app.go` are automatically exposed as callable functions from TypeScript. Auto-generated binding stubs live in `frontend/wailsjs/go/` (do not edit).
Bound services:
- `backend/frontendutil/frontendutil.go``@go/frontendutil/FrontendUtil` - Directory/file picker dialogs
- `backend/config/config.go``@go/config/Config` - Get/set all configuration
- `backend/library/library.go``@go/library/Library` - Library scanning and queries
- `backend/playlist/playlist.go``@go/playlist/Service` - Playlist CRUD
- `backend/queue/queue.go``@go/queue/Queue` - Queue management
- `backend/player/player.go``@go/player/Player` - Playback control (play, pause, seek, volume, load)
**State Synchronization Pattern:**
The backend is the source of truth. The frontend requests initial state after its stores are ready:
```typescript
// frontend/index.ts (after all stores import and register listeners)
void Player.EmitCurrentState();
void Queue.EmitCurrentState();
```
Backend responds by emitting the full current state via events, which the stores receive and cache.
## Data Storage
**Database: SQLite**
- Driver: `modernc.org/sqlite` v1.45.0 (pure-Go, no CGo)
- DB file: `~/.local/share/yellowjacket/yj.db` (Linux)
- Connection: `backend/database/database.go`
- Pragmas: WAL journal mode, `busy_timeout=5000`, `foreign_keys=ON`
- Constraint: `SetMaxOpenConns(1)` (single writer)
- Code generation: sqlc (`backend/database/sqlc.yaml`)
- Schemas: `backend/database/sql/schemas/*.sql` (30 schema files)
- Queries: `backend/database/sql/queries/*.sql` (15 query files)
- Generated output: `backend/database/sql/sqlcgen/` (DO NOT EDIT)
- Schema migration: Custom migration system using `PRAGMA user_version` (`backend/database/database.go`, `runMigrations()`)
- Migration 1: Audio file property columns (sample_rate, bit_depth, channels, bitrate, file_size)
- Migration 2: Basename column, FTS5 search index
**Database Schema (key tables):**
| Table | Purpose |
|---|---|
| `audio_files` | Tracks with file paths, metadata references, audio properties |
| `recordings` | Track metadata (title, track number, year, genre, etc.) |
| `artists` | Artist entities |
| `artist_credit` | Artist credit display names |
| `artist_credit_artist` | M:N link between artists and credits |
| `release_groups` | Albums |
| `release_group_recordings` | M:N link between albums and recordings |
| `cover_art` | Cover art file references |
| `genres` | Genre entities |
| `genre_recordings` | M:N link between genres and recordings |
| `playlists` / `playlist_tracks` | User playlists |
| `queue` / `queue_tracks` | Playback queue with persistence |
| `player_state` | Persisted player state (volume, last track, position) |
| `file_types` | Supported audio file type registry |
| `search_index` | FTS5 full-text search index (file_path, title, artist, album) |
**File Storage:**
- Cover art cache: `~/.local/share/yellowjacket/covers/` (Linux)
- Managed by `backend/coverart/coverart.go` and `backend/library/coverart.go`
- Size variants: original, `_sm` (small), `_md` (medium), `_lg` (large)
- Served via custom asset handler at `/covers/` prefix
- Config file: `~/.config/yellowjacket/config.toml` (Linux)
- Managed by `backend/config/config.go`
- Format: TOML via `github.com/BurntSushi/toml`
**Caching:**
- In-memory entity cache during library scans (`entityCache` in `backend/library/library.go`) - caches artist credits, artists, release groups, cover art, genres to avoid redundant DB upserts
- No external caching service
## Audio Playback
**Library: `github.com/gopxl/beep/v2` v2.1.1**
Core audio engine providing decode → resample → control → volume → speaker pipeline.
- Decoder: `backend/metadata/decoder.go` - Routes by file extension to beep decoders
- Player: `backend/player/player.go` - Manages streamer chain and playback state
- Speaker: Initialized at 44100 Hz sample rate, 100ms buffer (`time.Second/10`)
**Supported Formats:**
| Format | Decoder | Extension |
|---|---|---|
| MP3 | `github.com/gopxl/beep/v2/mp3` (via `github.com/hajimehoshi/go-mp3`) | `.mp3` |
| FLAC | `github.com/gopxl/beep/v2/flac` (via `github.com/mewkiz/flac`) | `.flac` |
| Ogg Vorbis | `github.com/gopxl/beep/v2/vorbis` (via `github.com/jfreymuth/oggvorbis`) | `.ogg` |
| WAV | `github.com/gopxl/beep/v2/wav` | `.wav` |
**Audio Pipeline (per track):**
1. File opened → decoded to `beep.StreamSeekCloser`
2. Resampled from source sample rate to speaker rate (44100 Hz, quality=4)
3. Wrapped in `beep.Ctrl` for play/pause control
4. Wrapped in `effects.Volume` for volume control (base=2, range -5 to 0 internal)
5. Registered with `speaker.Play()` with a `beep.Callback` for end-of-track notification
**Speaker hardware** uses `github.com/ebitengine/oto/v3` (indirect dependency via beep) for cross-platform audio output.
**Volume System:**
- User-facing: 0100 integer scale (`player.UserVolume`)
- Internal: -5.0 to 0.0 float scale (`player.Volume`)
- Conversion: `backend/player/volume.go`
## Metadata Extraction
**Library: `github.com/dhowden/tag`**
- Extracts ID3v2, Vorbis Comment, and FLAC tags
- Implementation: `backend/metadata/tags.go` (`ExtractTags`, `ExtractTagsFromReader`)
- Extracted fields: title, artist, album, album artist, composer, genre, year, track/disc numbers, lyrics, comment, embedded cover art
**Custom Duration Parsers:**
- MP3: `backend/metadata/mp3duration.go` - Custom header parser for accurate duration (handles multiple ID3v2 tags that inflate `go-mp3`'s `Len()`)
- FLAC: `backend/metadata/flacduration.go` - Custom FLAC STREAMINFO header parser
- General: `backend/metadata/duration.go` - Fallback using beep decoder for WAV/OGG
**Combined Extraction:**
- `backend/metadata/metadata.go``ExtractAllMetadata()` - Single-pass extraction of tags, duration, and audio properties (sample rate, bit depth, channels, bitrate, file size)
## System Integrations
### MPRIS2 Media Controls (Linux)
- Implementation: `backend/mediacontrols/mpris_linux.go` (`//go:build linux`)
- D-Bus library: `github.com/godbus/dbus/v5`
- Bus name: `org.mpris.MediaPlayer2.yellowjacket`
- Object path: `/org/mpris/MediaPlayer2`
- Interfaces: `org.mpris.MediaPlayer2` (root), `org.mpris.MediaPlayer2.Player`
- Capabilities: Play, Pause, PlayPause, Stop, Next, Previous, Seek, SetPosition, Volume, Metadata push
- Non-Linux: No-op stub (`backend/mediacontrols/stub.go`, `//go:build !linux`)
**Architecture:** All D-Bus property updates are dispatched via a buffered channel (`updateChanSize = 64`) to a dedicated goroutine, preventing deadlocks between the player mutex and godbus property mutex.
### File System
- Library scanning: `backend/library/library.go` - Recursive `fs.WalkDir` with concurrent worker pool (`errgroup`)
- Disk type detection: `backend/system/disktype_linux.go` / `backend/system/disktype_other.go` - Detects HDD vs SSD for adaptive scan concurrency
- User data directories: `backend/system/userdata.go` - OS-specific paths for config and data
- Native dialogs: `backend/frontendutil/frontendutil.go` - Directory picker, file picker (for M3U import)
### Playlist Import/Export
- M3U/M3U8 parsing: `backend/playlist/m3u.go`
- Playlist matching: `backend/playlist/match.go` - Fuzzy matching of playlist entries to library tracks
- Favorites system: `backend/playlist/favorites.go` - Special playlist designated as favorites
### Cover Art System
- Extraction: Embedded art from audio file tags (`backend/library/coverart.go`)
- Storage: Hash-based filenames in `~/.local/share/yellowjacket/covers/`
- Size variants: Small (100px), Medium (200px), Large (400px) - generated via `golang.org/x/image`
- Serving: Custom HTTP handler at `/covers/` prefix (`backend/coverart/handler.go`)
- URL resolution: `backend/coverart/coverart.go``ResolveURLs()` converts filesystem paths to URL paths
### Custom Asset Server
- Implementation: `backend/assets/handler.go`
- Serves embedded frontend dist files via Wails asset server
- Supports custom route registration (used by cover art handler)
- Middleware pattern captures Wails' default handler for fallback
## Frontend Architecture
### Entry Points
- Main app: `frontend/index.html``frontend/index.ts`
- View routing: DOM-based navigation via `navigate` CustomEvent in `frontend/index.ts`
- Views: tracks, albums, playlists, artists, genres, libraries, settings, artist-details, genre-details
### State Management
Singleton stores in `frontend/src/store/`:
- `player-store.ts` - Playback state, current track, volume
- `queue-store.ts` - Queue tracks, current index, play mode
- `library-store.ts` - Library track listing
- `playlist-store.ts` - Playlist data
- `favorites-store.ts` - Favorites state
- `theme-store.ts` - Theme accent color and background shade
- `search-store.ts` - Search query and results
- `tracklist-store.ts` - Track list column configuration
Each store subscribes to Wails events and delegates actions to backend via Wails bindings.
### ReactiveController Pattern
Controllers in `frontend/src/store/controllers/` connect Lit components to stores:
- `player-controller.ts`, `queue-controller.ts`, `library-controller.ts`, `playlist-controller.ts`, `favorites-controller.ts`, `theme-controller.ts`, `search-controller.ts`, `tracklist-controller.ts`
- Subscribe in `hostConnected()`, unsubscribe in `hostDisconnected()`
## Profiling & Observability
**Development Only (eliminated in production builds):**
- pprof HTTP server: `localhost:6060` (`backend/profiling/profiling.go`, `//go:build dev`)
- Endpoints: `/debug/pprof/`, `/debug/trace`
- Block and mutex profiling enabled
- Custom `TimeOp()` function for operation timing
**Logging:**
- Framework: `log/slog` (structured, key-value pairs)
- Dev handler: `github.com/golang-cz/devslog` (pretty-printed to stdout)
- Wails logger bridge: `backend/logging/logging.go` (routes Wails logs through slog)
- Pattern: Logger injected via constructors, scoped with `logger.WithGroup("component")`
## External APIs & Services
**None.** YellowJacket is a fully local, offline application. There are no external API calls, cloud services, analytics, telemetry, or network requests. All data lives on the local filesystem.
## CI/CD & Deployment
**CI Pipeline:** Not detected in the repository (no `.github/workflows/`, `.gitlab-ci.yml`, etc.)
**Git Hooks (lefthook):**
- `lefthook.yml` - Pre-commit: go vet, golangci-lint, codegen check, frontend typecheck
- Pre-push: protect main branch, go test, go mod verify
**Distribution:** Binary builds via `make build-prod` (obfuscated + UPX compressed)
## Webhooks & Callbacks
**Incoming:** None
**Outgoing:** None
---
*Integration audit: 2026-02-26*