From fa2183ed1809945e9755fd5b420b46d6d986cbe0 Mon Sep 17 00:00:00 2001 From: Kingkor Roy Tirtho Date: Tue, 3 Nov 2026 18:52:06 +0600 Subject: [PATCH] fix: crash at startup from a Koin cycle in DeviceAudioPlayerQueue #3170 made DeviceAudioPlayerQueue resolve StreamingUrlRepository eagerly in a property initializer, but StreamingUrlRepository is constructed with the AudioPlayerQueue (this queue). Koin recursed between the two definitions until the stack overflowed, crashing the app at launch (SIGSEGV in Koin's SingleInstanceFactory). The queue is common code, so every platform was affected. The dependency only existed for an iOS-only path that resolved every queue entry to a direct stream URL, bypassing the local stream proxy. That was an AVPlayer workaround; iOS now plays through VLC, which plays the proxied streams like the other platforms, so the path is removed and the queue is back to how it was before #3170. The iOS media session from #3170 is kept. --- .../audioplayer/DeviceAudioPlayerQueue.kt | 26 ++----------------- 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/audioplayer/DeviceAudioPlayerQueue.kt b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/audioplayer/DeviceAudioPlayerQueue.kt index 56344386..7798ab77 100644 --- a/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/audioplayer/DeviceAudioPlayerQueue.kt +++ b/composeApp/src/commonMain/kotlin/org/spotube/spotube/core/audioplayer/DeviceAudioPlayerQueue.kt @@ -18,10 +18,7 @@ package org.spotube.spotube.core.audioplayer import org.spotube.plugin_interfaces.plugin_apis.audio.StreamProtocol -import org.spotube.spotube.PlatformType -import org.spotube.spotube.getPlatform import org.spotube.spotube.core.di.injectLogger -import org.spotube.spotube.core.server.StreamingUrlRepository import org.spotube.spotube.modules.blacklist.BlacklistRepository import org.spotube.spotube.modules.library.local_tracks.media.LocalMediaCacheRepository import org.spotube.spotube.modules.plugin.PluginProvider @@ -54,7 +51,6 @@ class DeviceAudioPlayerQueue( private val blacklistRepository: BlacklistRepository) : AudioPlayerQueue, KoinComponent { private val logger by injectLogger() - private val streamingUrlRepository = get() private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) @@ -519,25 +515,7 @@ class DeviceAudioPlayerQueue( private suspend fun QueueEntry.toMediaItem(): MediaItem? { return when (this) { is QueueEntry.StreamingTrack -> { - val isIos = getPlatform().type == PlatformType.IOS - val requiresIosResolution = isIos && ( - url.isBlank() || - url.startsWith("http://127.0.0.1") || - url.startsWith("https://127.0.0.1") || - url.startsWith("http://[::1]") || - url.startsWith("https://[::1]") || - url.startsWith("http://localhost") || - url.startsWith("https://localhost")) - val resolvedStream = if (requiresIosResolution) { - streamingUrlRepository.resolveStreamInfo(track) - } else { - null - } - val resolvedUrl = if (requiresIosResolution) { - resolvedStream?.url ?: buildStreamingUrl(track.id, protocol) - } else { - url.ifBlank { buildStreamingUrl(track.id, protocol) } - } + val resolvedUrl = url.ifBlank { buildStreamingUrl(track.id, protocol) } if (resolvedUrl.isBlank()) return null MediaItem( title = track.title, @@ -546,7 +524,7 @@ class DeviceAudioPlayerQueue( duration = track.durationMs.milliseconds, coverURL = artworkUrl.orEmpty(), url = resolvedUrl, - protocol = resolvedStream?.protocol ?: protocol) + protocol = protocol) } is QueueEntry.LocalTrack -> {