A native macOS music client for Navidrome/Subsonic servers with Last.fm scrobbling support.
- Music Playback: Stream music from your Navidrome/Subsonic server
- Library Management: Browse artists, albums, songs, genres, and playlists
- Now Playing: Expanded player with album artwork, lyrics, and vinyl/album art toggle
- Picture-in-Picture: Floating player window that stays on top
- Smart Playlist: Drag-and-drop songs into playlists with multi-select support
- Formatting Control: Choose streaming format (MP3, AAC, FLAC, Opus) and bitrate for local and remote networks
- Favorites: Star songs for quick access
- Search: Full-text search across your library
- Last.fm Integration: Scrobble your plays to Last.fm
- Lyrics: Display synchronized lyrics when available
- Caching: Local cache for offline playback
- System Integration: Menu bar player, keyboard shortcuts, system media controls
- macOS 14.0+
- Navidrome or Subsonic server
- Swift 6.0
- Clone the repository
- Open
Lune.xcodeprojin Xcode - Build and run with
Cmd+B
- Open Settings > Server
- Enter your local and/or remote Navidrome/Subsonic server URLs
- Add your username and password
- Click "Test Connection" to verify
- Click "Save"
- Register an app at last.fm/api/account/create
- Open Settings > Last.fm
- Paste your API key and secret
- Click "Connect Last.fm" and authorize in your browser
- Click "I've authorized — Finish connecting"
- Toggle "Enable scrobbling" to start sending plays to Last.fm
- Open Settings > Playback
- Configure format and bitrate for local and remote streaming
- Enable/disable menu bar player
- Configure PiP (Picture-in-Picture) window behavior
- SwiftUI for the UI with macOS 14+ deployment target
- Swift 6 strict concurrency (actors, @MainActor, @Observable)
- AVFoundation for audio playback
- Subsonic API v1.16.1 for server communication
- UserDefaults for settings persistence
- Last.fm API for scrobbling
PlayerEngine: Central audio playback and queue managementSubsonicClient: Network layer for Subsonic API callsLastFMClient: Last.fm API integration for scrobblingLibraryStore: Manages music library cache and stateTrackCache: Local file caching for offline playbackImageCache: Two-tier image caching (memory + disk)
Lune/
├── API/ # Network clients (Subsonic, Last.fm, Lyrics)
├── App/ # Main app entry and content routing
├── Audio/ # Playback engine and bridge
├── Cache/ # Track and image caching
├── Components/ # Reusable UI components
├── Models/ # Data models
├── Persistence/ # Settings and configuration
├── Utilities/ # Helper functions
└── Views/ # UI views by feature
├── Library/ # Artists, albums, songs, genres
├── NowPlaying/ # Player UI
├── Playlists/ # Playlist management
├── Queue/ # Queue view
├── Search/ # Search interface
├── Settings/ # Settings and onboarding
└── Sidebar/ # Navigation sidebar
- Create new feature branches from
main - Build with
xcodebuild -scheme Lune build - Test in Xcode with Cmd+R
- Commit with clear messages
- Follow Swift style guidelines
- Use SwiftUI for new UI components
- Mark concurrent code with
@MainActorornonisolatedas appropriate - No external dependencies (uses system frameworks only)
Copyright (c) 2026 Sahil Saleem. Licensed under GPL-3.0: you may reuse, change and sell it, but anything you distribute that includes it must stay GPL-3.0 with source. See LICENSE.
Built with Navidrome 🎵