Files
yellowjacket/.planning/codebase/INTEGRATIONS.md
T
2026-02-26 16:58:25 -05:00

261 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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*