Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Boot embedded daemon from RockboxRpcModule.OnCreate, not on first play

NowPlayingService.onCreate only fires after JS calls RockboxNowPlaying.update(), which only happens once playback starts. RockboxRpcModule.OnCreate runs the moment the Expo module loads (app start) — much earlier and unconditional. Daemon now boots even if the user just opens the app and looks at the discovery list.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

+33
+3
expo/modules/rockbox-now-playing/android/src/main/java/expo/modules/rockboxnowplaying/NowPlayingService.kt
··· 109 109 override fun onCreate() { 110 110 super.onCreate() 111 111 ensureNotificationChannel() 112 + /* Daemon now boots from RockboxRpcModule.OnCreate (app start) instead 113 + * of here (first-playback). Keep bootEmbeddedDaemon() as a no-op-ish 114 + * fallback that just logs the daemon state — useful for debugging. */ 112 115 bootEmbeddedDaemon() 113 116 114 117 mediaSession = MediaSessionCompat(this, "RockboxNowPlaying").apply {
+30
expo/modules/rockbox-rpc/android/src/main/java/expo/modules/rockboxrpc/RockboxRpcModule.kt
··· 180 180 "rockbox.error", 181 181 ) 182 182 183 + // Boot the in-process rockbox daemon when the module loads (app start). 184 + // Calls rb_daemon_start (firmware boot + gRPC/HTTP/GraphQL/MPD servers 185 + // on 6061/6063/6062/6600) on a background thread so the Expo runtime 186 + // isn't blocked. The native call returns -38 (ENOSYS) on builds without 187 + // the embedded-daemon cargo feature — those keep the remote-only path. 188 + OnCreate { 189 + val ctx = appContext.reactContext ?: return@OnCreate 190 + scope.launch { 191 + try { 192 + val configDir = ctx.applicationContext.filesDir.absolutePath 193 + val musicDir = android.os.Environment 194 + .getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_MUSIC) 195 + .absolutePath 196 + val deviceName = android.os.Build.MODEL ?: "rockbox-android" 197 + android.util.Log.i("RockboxRpc", 198 + "OnCreate: starting embedded daemon (config=$configDir music=$musicDir)") 199 + val rc = rb_daemon_start(configDir, musicDir, deviceName) 200 + when { 201 + rc > 0 -> android.util.Log.i("RockboxRpc", "embedded daemon started, gRPC :$rc") 202 + rc == -38 -> android.util.Log.i("RockboxRpc", "embedded daemon not built into this .so (remote-only mode)") 203 + rc == -114 -> android.util.Log.i("RockboxRpc", "embedded daemon already running") 204 + else -> android.util.Log.w("RockboxRpc", "embedded daemon start failed rc=$rc") 205 + } 206 + } catch (t: Throwable) { 207 + android.util.Log.e("RockboxRpc", 208 + "embedded daemon threw: ${t.javaClass.simpleName}: ${t.message}", t) 209 + } 210 + } 211 + } 212 + 183 213 Function("rockboxServiceName") { 184 214 rb_rockbox_service_name() ?: "_rockbox._tcp.local." 185 215 }