12 KiB
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:
runtime.EventsEmit(p.ctx, events.TrackChanged, trackInfo)
runtime.EventsOn(l.ctx, events.LibraryConfigChanged, func(data ...any) { ... })
TypeScript event subscription pattern:
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 dialogsbackend/config/config.go→@go/config/Config- Get/set all configurationbackend/library/library.go→@go/library/Library- Library scanning and queriesbackend/playlist/playlist.go→@go/playlist/Service- Playlist CRUDbackend/queue/queue.go→@go/queue/Queue- Queue managementbackend/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:
// 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/sqlitev1.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)
- Schemas:
- 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.goandbackend/library/coverart.go - Size variants: original,
_sm(small),_md(medium),_lg(large) - Served via custom asset handler at
/covers/prefix
- Managed by
- Config file:
~/.config/yellowjacket/config.toml(Linux)- Managed by
backend/config/config.go - Format: TOML via
github.com/BurntSushi/toml
- Managed by
Caching:
- In-memory entity cache during library scans (
entityCacheinbackend/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):
- File opened → decoded to
beep.StreamSeekCloser - Resampled from source sample rate to speaker rate (44100 Hz, quality=4)
- Wrapped in
beep.Ctrlfor play/pause control - Wrapped in
effects.Volumefor volume control (base=2, range -5 to 0 internal) - Registered with
speaker.Play()with abeep.Callbackfor 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: 0–100 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 inflatego-mp3'sLen()) - 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- Recursivefs.WalkDirwith 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
navigateCustomEvent infrontend/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, volumequeue-store.ts- Queue tracks, current index, play modelibrary-store.ts- Library track listingplaylist-store.ts- Playlist datafavorites-store.ts- Favorites statetheme-store.ts- Theme accent color and background shadesearch-store.ts- Search query and resultstracklist-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 inhostDisconnected()
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