Navidrome
Find a file
2026-04-04 18:45:10 -07:00
iOS Update from NavidromePlayer.zip (2026-04-04 18:41) 2026-04-04 18:41:21 -07:00
Shared more fixes for radio 2026-04-04 18:45:10 -07:00
watchOS Update from NavidromePlayer.zip (2026-04-03 19:32) 2026-04-03 19:32:06 -07:00
.gitignore NavidromePlayer: iOS + watchOS Navidrome/Subsonic music player 2026-03-28 20:49:47 +00:00
AppIcon.png quick fix 2026-03-28 18:24:48 -07:00
generate.sh NavidromePlayer: iOS + watchOS Navidrome/Subsonic music player 2026-03-28 20:49:47 +00:00
project.yml Update from NavidromePlayer.zip (2026-04-04 06:58) 2026-04-04 06:58:58 -07:00
README.md NavidromePlayer: iOS + watchOS Navidrome/Subsonic music player 2026-03-28 20:49:47 +00:00
update.sh quick fix 2026-03-28 18:24:48 -07:00

NavidromePlayer

A native iOS + watchOS music player for Navidrome servers, built with SwiftUI and AVFoundation. Features a Mitsuha-style audio visualizer, Apple Watch companion app, radio streaming with Shazam identification, and an optional Companion API for advanced server-side features.

Features

iOS App

  • Full Navidrome/Subsonic library — browse by albums, artists, songs, genres, playlists
  • Offline playback — download songs for offline listening with per-track and per-album downloads
  • Mitsuha visualizer — real-time FFT waveform visualization with Catmull-Rom spline rendering, multiple styles (wave, bar, line), color modes (dynamic, album art, custom), and configurable presets
  • Now Playing — full-screen player with album art color extraction, drag-to-dismiss, AirPlay, seek bar, queue management
  • Mini player — persistent bottom bar with scrubbable progress, visualizer overlay, transport controls
  • Radio streaming — live radio with HLS/PLS/M3U playlist resolution, timeshift buffering, recording, and Shazam identification
  • Smart DJ — crossfade engine with silence skipping, loudness normalization (LUFS), and predictive transitions (requires Companion API)
  • Batch metadata editing — edit album/artist tags across multiple albums simultaneously (requires Companion API)
  • Custom album covers — long-press album art to replace with photos from your library
  • Multi-server support — automatic failover between servers with the same credentials
  • Background audio — Lock Screen / Control Center controls, Dynamic Island support
  • Keyboard dismiss — tap anywhere outside text fields or scroll to dismiss

watchOS App

  • Offline playback — transfer songs from iPhone to Apple Watch for standalone listening
  • Bluetooth + Speaker mode — Apple Watch Ultra speaker support via HKWorkoutSession
  • Crown control — Digital Crown volume control with haptic feedback
  • Compact visualizer — waveform visualization on watch face

Companion API (Optional)

A Python FastAPI server that runs alongside Navidrome on your server (e.g., Raspberry Pi) providing:

  • Smart DJ analysis — BPM detection, silence boundary mapping, LUFS loudness measurement
  • Mitsuha visualizer pre-computation — generate FFT frames on the server instead of on-device
  • Remote ID3 tag editing — edit metadata on server files via mutagen
  • File uploads — upload and auto-tag new music via the app
  • WebSocket push — real-time notifications to the iOS app when metadata changes or uploads complete
  • Navidrome scan trigger — automatically rescan after tag edits

Architecture

NavidromePlayer/
├── Shared/                          # Code shared between iOS and watchOS
│   ├── API/SubsonicClient.swift     # Full Subsonic/Navidrome REST client
│   ├── Audio/AudioPlayer.swift      # AVPlayer + AVAudioEngine, FFT, visualizer levels
│   ├── Models/Models.swift          # Codable models for all API responses
│   └── Storage/
│       ├── LibraryCache.swift       # Disk cache for instant offline browsing
│       ├── OfflineManager.swift     # Download manager with progress tracking
│       ├── ServerManager.swift      # Multi-server with auto-failover
│       └── WatchConnectivityManager.swift  # WCSession file transfers
│
├── iOS/
│   ├── App/NavidromePlayerApp.swift # Entry point, background upload delegate
│   └── Views/
│       ├── Common/                  # MainTabView, MiniPlayer, AsyncCoverArt, DebugConsole
│       ├── Companion/               # Companion API integration
│       │   ├── CompanionAPIService.swift    # API client + WebSocket push client
│       │   ├── CompanionSettingsView.swift  # Config, Smart DJ toggles, analysis triggers
│       │   ├── SmartCrossfadeManager.swift  # Dual AVPlayer A/B crossfade engine
│       │   ├── TrackEditorView.swift        # Single-track metadata editor
│       │   ├── BatchAlbumEditorSheet.swift  # Edit tags for one album
│       │   ├── MultiAlbumEditorSheet.swift  # Batch edit across multiple albums
│       │   ├── BatchUploadView.swift        # Zip import + batch upload
│       │   └── ZipImportManager.swift       # Background URLSession uploads
│       ├── Library/                 # MyMusic, Albums, Artists, Playlists, Search, Radio, Downloads
│       ├── Login/                   # Server configuration
│       ├── NowPlaying/              # Full player, SiriSeekBar, RadioStreamBuffer, Shazam
│       └── Visualizer/             # MitsuhaVisualizerView, OfflineAudioAnalyzer, storage
│
├── watchOS/
│   ├── App/                        # Watch app entry, offline store, session manager
│   ├── Audio/WatchAudioPlayer.swift # Dual mode: Bluetooth + Ultra speaker
│   └── Views/                      # Library, NowPlaying, Setup, Visualizer
│
├── project.yml                     # XcodeGen project definition
└── generate.sh                     # Regenerate .xcodeproj

Requirements

  • iOS 17.0+ / watchOS 10.0+
  • Xcode 15+
  • XcodeGenbrew install xcodegen
  • Navidrome server — any version with Subsonic API support

Building

./generate.sh
# Opens Xcode automatically. Select device and build.

Set your Apple Developer Team ID in project.yml under DEVELOPMENT_TEAM.


Companion API

The Companion API is optional. Without it, the app works as a standard Navidrome player. With it, you get Smart DJ, tag editing, uploads, and server-side visualizer pre-computation.

Server Directory Structure

/home/pi/docker/navidrome/
├── docker-compose.yml               # Both Navidrome + Companion
├── navidrome_data/                   # Navidrome database (auto-created)
├── companion_data/                   # Persistent data (auto-created)
│   ├── smart_dj.db                   # BPM, silence, loudness profiles
│   └── vis_cache/                    # Pre-computed Mitsuha FFT frames
└── companion_api/                    # Companion API source code
    ├── Dockerfile
    ├── main.py
    └── pre_analyze.py

/home/pi/navidrome/music/             # Your music library
├── Artist/Album/song.flac
└── ...

docker-compose.yml

services:
  navidrome:
    image: deluan/navidrome:latest
    container_name: navidrome
    restart: unless-stopped
    ports:
      - "4533:4533"
    environment:
      - ND_SCANSCHEDULE=1h
      - ND_BASEURL=/navidrome
    volumes:
      - /home/pi/navidrome:/music:ro
      - ./navidrome_data:/data

  music-companion:
    build: ./companion_api
    container_name: music-companion
    restart: unless-stopped
    ports:
      - "8000:8000"
    volumes:
      - /home/pi/navidrome/music:/music:rw
      - ./companion_data:/app/data
    environment:
      - MUSIC_DIR=/music
      - DB_PATH=/app/data/smart_dj.db
      - VIS_CACHE_DIR=/app/data/vis_cache
      - NAVIDROME_URL=http://navidrome:4533/navidrome
      - SUBSONIC_USER=your_username
      - SUBSONIC_TOKEN=your_token
      - SUBSONIC_SALT=your_salt
    depends_on:
      - navidrome

Volume mount note: The companion mounts /home/pi/navidrome/music directly (not the parent /home/pi/navidrome). This ensures MUSIC_DIR=/music maps to your actual music files without path prefix issues.

Commands

cd /home/pi/docker/navidrome

# ── Startup ──────────────────────────────────────────────
docker compose up -d                                        # Start everything
docker compose up -d --build music-companion                # Rebuild after code changes
docker compose logs -f music-companion                      # View logs

# ── Health Check ─────────────────────────────────────────
curl http://localhost:8000/health
# Returns: profiles count, vis cache count, connected clients

# ── Pre-Analysis (run after adding new music) ────────────
docker compose exec music-companion python pre_analyze.py           # Analyze missing (DJ + vis)
docker compose exec music-companion python pre_analyze.py --dj      # DJ profiles only (faster)
docker compose exec music-companion python pre_analyze.py --vis     # Visualizer frames only
docker compose exec music-companion python pre_analyze.py --force   # Re-analyze everything

# ── Reset Analysis Data ─────────────────────────────────
rm companion_data/smart_dj.db                               # Delete DJ profiles
rm -rf companion_data/vis_cache/*                           # Delete vis frame cache
docker compose exec music-companion python pre_analyze.py   # Regenerate from scratch

# ── Maintenance ──────────────────────────────────────────
docker compose restart music-companion                      # Restart API
docker compose down                                         # Stop everything
docker compose down -v                                      # Stop + remove volumes

API Endpoints

Method Endpoint Description
GET /health Server status, profile count, vis cache count, connected clients
PATCH /edit-metadata Edit ID3 tags (JSON body: relative_path, title, artist, album, etc.)
POST /upload-track Upload audio file with metadata (multipart: file, title, artist, album)
GET /smart-dj/profile?relative_path=... BPM, silence start/end, LUFS for a track
GET /smart-dj/bulk-profiles?paths=... Batch fetch profiles (comma-separated paths)
GET /visualizer/frames?relative_path=... Pre-computed Mitsuha FFT frames (JSON array)
POST /visualizer/precompute Trigger background vis frame generation for all tracks
POST /bulk-fix Trigger Navidrome library rescan
WS /ws/push Real-time push events to iOS app

Path Resolution

Navidrome's song.path field can differ from the actual filesystem path. The Companion API uses a three-strategy resolver:

  1. Direct joinMUSIC_DIR + relative_path
  2. Strip prefix — removes leading components one at a time (handles Navidrome's library folder prefix)
  3. Filename search — walks the music directory looking for an exact filename match

If a 404 still occurs, the error response includes the exact paths that were tried for debugging.

iOS App Configuration

  1. SettingsCompanion API
  2. Enter your server's IP address and port (default: 8000)
  3. Toggle Enable Companion API
  4. Tap Test Connection to verify
  5. Toggle Smart DJ for crossfade and analysis features

Feature Details

Mitsuha Visualizer

The visualizer renders smooth liquid waveforms using Catmull-Rom splines with configurable tension. FFT data comes from one of three sources:

  • Real-time engine FFT — AVAudioEngine tap on local files
  • Offline pre-analyzed frames — cached FFT data synced to playback position
  • Server-computed frames — downloaded from Companion API (saves device battery)

Settings include per-view configuration for Now Playing and Mini Player independently, with four built-in presets. The mini player visualizer automatically pauses when the full-screen Now Playing view is open (battery optimization).

Radio

  • Playlist resolution — resolves .pls, .m3u, .asx playlist URLs to direct stream URLs
  • HLS detection — identifies HLS streams from URL extension or content-type, disables raw buffering
  • Timeshift — buffer live radio and scrub back through recent audio
  • Recording — capture radio segments to local files
  • Shazam — identify currently playing content via SHSession + dedicated AVAudioEngine mic tap
  • Recorded playback — recorded radio files play with ±5s skip buttons instead of prev/next
  • Live toggle — start/stop buffering, snap to live edge

Batch Tag Editing

Long-press any album to:

  • Edit Album — edit tags for all tracks in that album
  • Select Albums — enter multi-select mode (nav bar transforms: Cancel / count / Edit button), tap albums to select, "Select All" toggle, then apply changes across all selected albums

The "Set same artist on all tracks" toggle fixes compilation albums that split into separate per-artist entries in Navidrome.

Downloads

Split into two sub-tabs:

  • Offline — storage usage, downloaded songs with playback, swipe to delete
  • Watch — Apple Watch connection status, pending transfers with progress, songs on watch, send all / delete all

Debug Console

Toggle in Settings → Developer. Two display modes:

  • Docked — panel above the tab bar, drag handle to resize (120500pt)
  • PiP — floating draggable/resizable window with collapse, dock-back, and close buttons

Caching

All views use cache-first loading to eliminate spinners on warm launches:

  • LibraryCache — albums, artists, playlists, genres, album/artist details as JSON
  • ImageCache — two-tier NSCache + disk JPEG with 200MB limit and LRU eviction
  • SmartDJCache — Smart DJ profiles cached locally per song path
  • AlbumCoverStore — user-set custom album covers in Documents
  • VisualizerStorageManager — pre-analyzed FFT frames per song

Detail views load from cache instantly, refresh from server silently, and show a Retry button if both cache and server are unavailable.


License

Personal project. Not affiliated with Navidrome.