From 4d38d9ac6235b95523b75dff9225ab081948d88d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 19:12:33 +0100 Subject: [PATCH 01/46] Give media playback one owner at a time Spotify is coming as a second media provider, and nothing currently stops two players from holding a session and talking over each other. Add a neutral MediaSessionOwner (identity, notification slot, active flag, pause) plus a process-wide registry that pauses every other active owner when one starts. MusicPlayback implements it: it claims the session in publish() and releases it on pause/stop. MusicService takes its channel and notification id from the owner instead of private constants, so a second provider can register its own slot without touching the service. No track/queue types cross the boundary; the Music UI keeps MusicPlayer. --- .../sidekit/apps/media/MediaSessionOwner.kt | 82 ++++++++++++ .../sidekit/apps/music/MusicPlayback.kt | 21 +++- .../sidekit/apps/music/MusicService.kt | 15 ++- .../apps/media/MediaSessionRegistryTest.kt | 119 ++++++++++++++++++ 4 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/media/MediaSessionOwner.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/media/MediaSessionRegistryTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/media/MediaSessionOwner.kt b/app/src/main/java/hrln/interactive/sidekit/apps/media/MediaSessionOwner.kt new file mode 100644 index 00000000..d240ae78 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/media/MediaSessionOwner.kt @@ -0,0 +1,82 @@ +package hrln.interactive.sidekit.apps.media + +/** + * Anything in the process that can hold the platform media session: local Music today, a remote + * provider later. + * + * Deliberately knows nothing about tracks, queues or media sources — only what the process needs + * to keep two players from talking over each other: an identity, the notification slot it uses, + * whether it is currently playing, and how to make it stop. + * + * Main thread only, like the players that implement it. + */ +interface MediaSessionOwner { + /** Stable identity, e.g. "music". Unique within the process. */ + val sessionId: String + + /** Notification channel this owner publishes on. Distinct per owner. */ + val notificationChannelId: String + + /** User-visible name of [notificationChannelId], used when the channel is created. */ + val notificationChannelName: String + + /** Notification id this owner publishes on. Distinct per owner. */ + val notificationId: Int + + /** True while this owner is actually producing audio. */ + val isActive: Boolean + + /** Stops playback without clearing the queue, so the user can resume where they were. */ + fun pause() +} + +/** + * The process's media sessions. Only one owner is active at a time: activating one pauses the + * others. Registration is explicit so an owner that never plays is still known (and can be + * paused) the first time another takes over. + * + * A plain class rather than an object so tests can hold their own registry; [MediaSessions] is + * the process-wide one. + * + * Main thread only. + */ +open class MediaSessionRegistry { + private val owners = LinkedHashSet() + + /** The owner currently allowed to play, or null. */ + var active: MediaSessionOwner? = null + private set + + val registered: List get() = owners.toList() + + fun register(owner: MediaSessionOwner) { + owners += owner + } + + fun unregister(owner: MediaSessionOwner) { + owners -= owner + if (active === owner) active = null + } + + /** + * Makes [owner] the active session. Every other owner that is playing is paused first, so + * starting one provider stops the other rather than mixing them. + */ + fun activate(owner: MediaSessionOwner) { + if (active === owner) return + // Claim first: a paused owner calls deactivate() from inside pause(), which must not + // clear the owner that just took over. + active = owner + owners.toList() + .filter { it !== owner && it.isActive } + .forEach { it.pause() } + } + + /** Clears [owner] if it is still the active session. */ + fun deactivate(owner: MediaSessionOwner) { + if (active === owner) active = null + } +} + +/** The one registry for the process. Main thread only. */ +object MediaSessions : MediaSessionRegistry() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt index 63508588..56f392ee 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt @@ -16,6 +16,8 @@ import android.os.SystemClock import androidx.compose.runtime.Immutable import hrln.interactive.sidekit.apps.media.AudioFocus import hrln.interactive.sidekit.apps.media.MediaPlayerCore +import hrln.interactive.sidekit.apps.media.MediaSessionOwner +import hrln.interactive.sidekit.apps.media.MediaSessions import hrln.interactive.sidekit.apps.media.Thumbnails import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -76,7 +78,13 @@ interface MusicPlayer { * * Main thread only. */ -class MusicPlayback private constructor(private val context: Context) : MusicPlayer { +class MusicPlayback private constructor(private val context: Context) : MusicPlayer, MediaSessionOwner { + override val sessionId = "music" + override val notificationChannelId = "music" + override val notificationChannelName = "Music playback" + override val notificationId = 0x6D75 // "mu" + override val isActive: Boolean get() = _state.value.isPlaying + private val _state = MutableStateFlow(PlaybackState()) override val state: StateFlow = _state.asStateFlow() @@ -133,7 +141,7 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla publish(s.copy(isPlaying = true, positionMs = c.positionMs, anchorAt = now())) } - fun pause() { + override fun pause() { val s = _state.value if (!s.isPlaying) return val position = s.positionAt(now()) @@ -145,6 +153,7 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla /** Ends playback and drops the queue: notification and session go away. */ fun stop() { + MediaSessions.deactivate(this) main.removeCallbacks(releaseIdle) releasePlayer() focus.abandon() @@ -258,6 +267,9 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla private fun publish(next: PlaybackState) { _state.value = next + // Claim (or release) the process media session before anyone reacts to the state, so a + // second provider pauses this one — and this one pauses others — exactly once. + if (next.isPlaying) MediaSessions.activate(this) else MediaSessions.deactivate(this) updateNoisy(next.isPlaying) if (!next.queue.isEmpty) updateSession(next) MusicService.sync(context, next) @@ -340,7 +352,10 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla @Volatile private var instance: MusicPlayback? = null fun get(context: Context): MusicPlayback = instance ?: synchronized(this) { - instance ?: MusicPlayback(context.applicationContext).also { instance = it } + instance ?: MusicPlayback(context.applicationContext).also { + instance = it + MediaSessions.register(it) + } } /** The player if music has been used in this process; never creates one. */ diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt index 18b0f41a..f329ad1b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt @@ -28,10 +28,11 @@ class MusicService : Service() { override fun onCreate() { super.onCreate() running = this + val playback = MusicPlayback.get(this) val manager = getSystemService(NotificationManager::class.java) - if (manager.getNotificationChannel(CHANNEL) == null) { + if (manager.getNotificationChannel(playback.notificationChannelId) == null) { manager.createNotificationChannel( - NotificationChannel(CHANNEL, "Music playback", NotificationManager.IMPORTANCE_LOW).apply { + NotificationChannel(playback.notificationChannelId, playback.notificationChannelName, NotificationManager.IMPORTANCE_LOW).apply { setShowBadge(false) }, ) @@ -42,7 +43,7 @@ class MusicService : Service() { val playback = MusicPlayback.get(this) // Started with startForegroundService: must enter the foreground before anything else. // (Even when about to stop: a service started that way that never does is a crash.) - startForeground(NOTIFICATION_ID, notification(playback, playback.state.value), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) + startForeground(playback.notificationId, notification(playback, playback.state.value), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) when (intent?.action) { ACTION_PLAY_PAUSE -> playback.togglePlayPause() ACTION_NEXT -> playback.next() @@ -65,9 +66,9 @@ class MusicService : Service() { stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() } - state.isPlaying -> startForeground(NOTIFICATION_ID, notification(playback, state), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) + state.isPlaying -> startForeground(playback.notificationId, notification(playback, state), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) else -> { - getSystemService(NotificationManager::class.java).notify(NOTIFICATION_ID, notification(playback, state)) + getSystemService(NotificationManager::class.java).notify(playback.notificationId, notification(playback, state)) stopForeground(STOP_FOREGROUND_DETACH) } } @@ -81,7 +82,7 @@ class MusicService : Service() { Intent(this, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_IMMUTABLE, ) - return Notification.Builder(this, CHANNEL) + return Notification.Builder(this, playback.notificationChannelId) .setSmallIcon(SidekitIcon.MusicNote2.drawable) .setContentTitle(track?.title.orEmpty()) .setContentText(track?.artist.orEmpty()) @@ -113,8 +114,6 @@ class MusicService : Service() { ) companion object { - private const val CHANNEL = "music" - private const val NOTIFICATION_ID = 0x6D75 // "mu" private const val ACTION_PLAY_PAUSE = "hrln.interactive.sidekit.music.PLAY_PAUSE" private const val ACTION_NEXT = "hrln.interactive.sidekit.music.NEXT" private const val ACTION_PREVIOUS = "hrln.interactive.sidekit.music.PREVIOUS" diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/media/MediaSessionRegistryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/media/MediaSessionRegistryTest.kt new file mode 100644 index 00000000..43f1c831 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/media/MediaSessionRegistryTest.kt @@ -0,0 +1,119 @@ +package hrln.interactive.sidekit.apps.media + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotSame +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Test + +class MediaSessionRegistryTest { + private class FakeOwner(override val sessionId: String) : MediaSessionOwner { + override val notificationChannelId = "channel-$sessionId" + override val notificationChannelName = "Channel $sessionId" + override val notificationId = sessionId.hashCode() + override var isActive = false + var pauses = 0 + + override fun pause() { + pauses++ + isActive = false + } + } + + @Test + fun `activating an owner pauses every other active owner`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music").apply { isActive = true } + val remote = FakeOwner("remote") + registry.register(music) + registry.register(remote) + + registry.activate(remote) + + assertEquals(1, music.pauses) + assertEquals(0, remote.pauses) + assertSame(remote, registry.active) + } + + @Test + fun `activating twice does not pause the owner again`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music").apply { isActive = true } + registry.register(music) + + registry.activate(music) + registry.activate(music) + + assertEquals(0, music.pauses) + assertSame(music, registry.active) + } + + @Test + fun `owners that are already paused are left alone`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music") + registry.register(music) + + val remote = FakeOwner("remote") + registry.register(remote) + + registry.activate(remote) + + assertEquals(0, music.pauses) + assertNotSame(music, registry.active) + } + + @Test + fun `deactivating a non-active owner keeps the active session`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music").apply { isActive = true } + val remote = FakeOwner("remote").apply { isActive = true } + registry.register(music) + registry.register(remote) + registry.activate(remote) + + registry.deactivate(music) + + assertSame(remote, registry.active) + } + + @Test + fun `deactivating the active owner clears it`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music").apply { isActive = true } + registry.register(music) + registry.activate(music) + + registry.deactivate(music) + + assertNull(registry.active) + } + + @Test + fun `unregistering the active owner clears it`() { + val registry = MediaSessionRegistry() + val music = FakeOwner("music") + registry.register(music) + registry.activate(music) + + registry.unregister(music) + + assertNull(registry.active) + assertEquals(emptyList(), registry.registered) + } + + @Test + fun `release order is irrelevant - whoever activates last wins`() { + val registry = MediaSessionRegistry() + val a = FakeOwner("a").apply { isActive = true } + val b = FakeOwner("b").apply { isActive = true } + registry.register(a) + registry.register(b) + + registry.activate(a) + registry.activate(b) + + assertEquals(1, a.pauses) + assertSame(b, registry.active) + } +} From 751beae2ff0b3d5244d22c4d628b4ea4a0a24895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 19:13:37 +0100 Subject: [PATCH 02/46] Move the Keystore cipher out of Banking PrivateSettings already encrypts with KeystoreCipher, which lived in BankingRepository.kt, so every app that wants sealed storage was importing the banking app for it. Move the cipher into apps/integrations next to PrivateSettings, name the interface SecretCipher since it is not banking-specific, and leave banking a BookCipher typealias so its call sites don't change. The default key alias stays "sidekit_banking" so existing banking.sealed files still open. --- .../sidekit/apps/banking/BankingRepository.kt | 59 ++--------------- .../apps/integrations/PrivateSettings.kt | 1 - .../sidekit/apps/integrations/SecretCipher.kt | 63 +++++++++++++++++++ 3 files changed, 67 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt index aa4a786f..c677489d 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt @@ -1,9 +1,9 @@ package hrln.interactive.sidekit.apps.banking import android.content.Context -import android.security.keystore.KeyGenParameterSpec -import android.security.keystore.KeyProperties import androidx.core.util.AtomicFile +import hrln.interactive.sidekit.apps.integrations.KeystoreCipher +import hrln.interactive.sidekit.apps.integrations.SecretCipher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -20,10 +20,6 @@ import java.io.IOException import java.security.GeneralSecurityException import java.security.KeyStore import java.util.concurrent.atomic.AtomicReference -import javax.crypto.Cipher -import javax.crypto.KeyGenerator -import javax.crypto.SecretKey -import javax.crypto.spec.GCMParameterSpec /** Connector metadata for the editable manual book. Up bank snapshots use [UpBankConnector] * separately so imported balances cannot be changed by local ledger entries. */ @@ -43,55 +39,8 @@ object ManualTracker : BankConnector { override suspend fun sync(): BankBook? = null } -/** Encrypts the Banking file. */ -interface BookCipher { - fun encrypt(plain: ByteArray): ByteArray - fun decrypt(sealed: ByteArray): ByteArray -} - -/** - * AES-256-GCM with a key generated inside the Android Keystore: the key never leaves secure - * hardware (or the keystore daemon), so a copied file is unreadable off this phone. Output is - * the 12-byte IV followed by ciphertext and tag. - * - * The key isn't bound to user authentication: Banking asks for the screen lock before it reads - * the file instead (see [BankingGate]); binding the key would make every background save fail - * once the auth window lapsed. - */ -class KeystoreCipher(private val alias: String = "sidekit_banking") : BookCipher { - private fun key(): SecretKey { - val store = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } - (store.getEntry(alias, null) as? KeyStore.SecretKeyEntry)?.let { return it.secretKey } - val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") - generator.init( - KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) - .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) - .setKeySize(256) - .build(), - ) - return generator.generateKey() - } - - override fun encrypt(plain: ByteArray): ByteArray { - val cipher = Cipher.getInstance(TRANSFORMATION) - cipher.init(Cipher.ENCRYPT_MODE, key()) - val iv = cipher.iv - return iv + cipher.doFinal(plain) - } - - override fun decrypt(sealed: ByteArray): ByteArray { - if (sealed.size <= IV_BYTES) throw GeneralSecurityException("Truncated") - val cipher = Cipher.getInstance(TRANSFORMATION) - cipher.init(Cipher.DECRYPT_MODE, key(), GCMParameterSpec(128, sealed, 0, IV_BYTES)) - return cipher.doFinal(sealed, IV_BYTES, sealed.size - IV_BYTES) - } - - private companion object { - const val TRANSFORMATION = "AES/GCM/NoPadding" - const val IV_BYTES = 12 - } -} +/** Banking's view of the shared cipher. */ +typealias BookCipher = SecretCipher /** Where the book lives. Called off the main thread only. */ interface BankStore { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt index c38188a9..ed3a29b6 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt @@ -2,7 +2,6 @@ package hrln.interactive.sidekit.apps.integrations import android.content.Context import androidx.core.util.AtomicFile -import hrln.interactive.sidekit.apps.banking.KeystoreCipher import org.json.JSONObject import java.io.File diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt new file mode 100644 index 00000000..1e7d2440 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt @@ -0,0 +1,63 @@ +package hrln.interactive.sidekit.apps.integrations + +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import java.security.GeneralSecurityException +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import javax.crypto.spec.GCMParameterSpec + +/** Encrypts one app's private file. Implementations must be safe to call from any thread. */ +interface SecretCipher { + fun encrypt(plain: ByteArray): ByteArray + fun decrypt(sealed: ByteArray): ByteArray +} + +/** + * AES-256-GCM with a key generated inside the Android Keystore: the key never leaves secure + * hardware (or the keystore daemon), so a copied file is unreadable off this phone. Output is + * the 12-byte IV followed by ciphertext and tag. + * + * The key isn't bound to user authentication: callers that need a screen lock ask for it before + * reading the file (see Banking's gate). Binding the key would make every background save fail + * once the auth window lapsed. + * + * The default alias is Banking's historical key so existing sealed files keep opening; every + * other app passes its own alias. + */ +class KeystoreCipher(private val alias: String = "sidekit_banking") : SecretCipher { + private fun key(): SecretKey { + val store = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } + (store.getEntry(alias, null) as? KeyStore.SecretKeyEntry)?.let { return it.secretKey } + val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore") + generator.init( + KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setKeySize(256) + .build(), + ) + return generator.generateKey() + } + + override fun encrypt(plain: ByteArray): ByteArray { + val cipher = Cipher.getInstance(TRANSFORMATION) + cipher.init(Cipher.ENCRYPT_MODE, key()) + val iv = cipher.iv + return iv + cipher.doFinal(plain) + } + + override fun decrypt(sealed: ByteArray): ByteArray { + if (sealed.size <= IV_BYTES) throw GeneralSecurityException("Truncated") + val cipher = Cipher.getInstance(TRANSFORMATION) + cipher.init(Cipher.DECRYPT_MODE, key(), GCMParameterSpec(128, sealed, 0, IV_BYTES)) + return cipher.doFinal(sealed, IV_BYTES, sealed.size - IV_BYTES) + } + + private companion object { + const val TRANSFORMATION = "AES/GCM/NoPadding" + const val IV_BYTES = 12 + } +} From 45e45ad130f0586ab5a138b010560f842def6dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 19:14:54 +0100 Subject: [PATCH 03/46] Report a missing native library instead of crashing System.loadLibrary ran from EmulatorCore's initialiser, and the emulator caught only Exceptions, so a build without that ABI died with an UnsatisfiedLinkError before any UI could say why. NativeLibraries loads once, caches success and failure, and returns false. The emulator checks it and shows "not available in this build". The Spotify bridge needs the same convention: arm64-only device builds and x86_64 emulator builds will each miss the other's library. --- .../sidekit/system/NativeLibraries.kt | 34 +++++++++++++++++++ .../sidekit/system/NativeLibrariesTest.kt | 18 ++++++++++ 2 files changed, 52 insertions(+) create mode 100644 app/src/main/java/hrln/interactive/sidekit/system/NativeLibraries.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/system/NativeLibrariesTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/system/NativeLibraries.kt b/app/src/main/java/hrln/interactive/sidekit/system/NativeLibraries.kt new file mode 100644 index 00000000..0be3c0a2 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/system/NativeLibraries.kt @@ -0,0 +1,34 @@ +package hrln.interactive.sidekit.system + +/** + * Loads the app's native libraries without taking the process down when one is missing. + * + * A build only carries the ABIs it was compiled for, so `System.loadLibrary` can legitimately + * fail on an emulator or a split APK. Callers ask once and show a "not available in this build" + * state instead of crashing from an object initialiser. + * + * Results are cached, including failures: a library that is not packaged will not appear later. + * Thread-safe; loading is idempotent per name. + */ +object NativeLibraries { + private val loaded = HashSet() + private val failed = HashSet() + + /** True if [name] (without the `lib` prefix and `.so` suffix) was loaded. */ + @Synchronized + fun load(name: String): Boolean { + if (name in loaded) return true + if (name in failed) return false + return try { + System.loadLibrary(name) + loaded += name + true + } catch (_: UnsatisfiedLinkError) { + failed += name + false + } + } + + @Synchronized + fun isAvailable(name: String): Boolean = name in loaded +} diff --git a/app/src/test/java/hrln/interactive/sidekit/system/NativeLibrariesTest.kt b/app/src/test/java/hrln/interactive/sidekit/system/NativeLibrariesTest.kt new file mode 100644 index 00000000..3bac4786 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/system/NativeLibrariesTest.kt @@ -0,0 +1,18 @@ +package hrln.interactive.sidekit.system + +import org.junit.Assert.assertFalse +import org.junit.Test + +class NativeLibrariesTest { + @Test + fun `a missing library reports unavailable instead of throwing`() { + assertFalse(NativeLibraries.load("sidekit_no_such_library")) + assertFalse(NativeLibraries.isAvailable("sidekit_no_such_library")) + } + + @Test + fun `repeated loads of a missing library stay false`() { + assertFalse(NativeLibraries.load("sidekit_no_such_library")) + assertFalse(NativeLibraries.load("sidekit_no_such_library")) + } +} From 814a4ef2f5a0556cff6aae00170f6edbfce5873a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 19:19:36 +0100 Subject: [PATCH 04/46] Format the files the seam work touched spotlessCheck ratchets from origin/main, so any file this branch changes must satisfy ktlint. These are formatter-only rewrites (trailing commas, brace placement, import order) in BankingRepository, PrivateSettings, SecretCipher, MusicPlayback and MusicService. Unrelated files the formatter also rewrites on this branch are left alone; they were already out of date before this work. --- .../sidekit/apps/banking/BankingRepository.kt | 18 +++++------ .../apps/integrations/PrivateSettings.kt | 18 +++++++++-- .../sidekit/apps/integrations/SecretCipher.kt | 2 +- .../sidekit/apps/music/MusicPlayback.kt | 31 +++++++++++-------- .../sidekit/apps/music/MusicService.kt | 12 ++++--- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt index c677489d..ef4b65b2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/banking/BankingRepository.kt @@ -4,6 +4,12 @@ import android.content.Context import androidx.core.util.AtomicFile import hrln.interactive.sidekit.apps.integrations.KeystoreCipher import hrln.interactive.sidekit.apps.integrations.SecretCipher +import java.io.File +import java.io.FileNotFoundException +import java.io.IOException +import java.security.GeneralSecurityException +import java.security.KeyStore +import java.util.concurrent.atomic.AtomicReference import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -14,12 +20,6 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import java.io.File -import java.io.FileNotFoundException -import java.io.IOException -import java.security.GeneralSecurityException -import java.security.KeyStore -import java.util.concurrent.atomic.AtomicReference /** Connector metadata for the editable manual book. Up bank snapshots use [UpBankConnector] * separately so imported balances cannot be changed by local ledger entries. */ @@ -94,7 +94,7 @@ class BankingRepository( scope: CoroutineScope, private val io: CoroutineDispatcher = Dispatchers.IO, private val clock: () -> Long = System::currentTimeMillis, - val connector: BankConnector = ManualTracker, + val connector: BankConnector = ManualTracker ) { sealed interface Load { data object Locked : Load @@ -190,7 +190,7 @@ class BankingRepository( b.copy( accounts = accounts, transactions = b.transactions.filter { it.accountId != id }, - selectedAccountId = if (b.selectedAccountId == id) accounts.firstOrNull()?.id else b.selectedAccountId, + selectedAccountId = if (b.selectedAccountId == id) accounts.firstOrNull()?.id else b.selectedAccountId ) } return removed @@ -251,7 +251,7 @@ object Banking { fun repository(context: Context): BankingRepository = repository ?: synchronized(this) { repository ?: BankingRepository( EncryptedFileBankStore(File(context.applicationContext.filesDir, "banking.sealed"), KeystoreCipher()), - CoroutineScope(SupervisorJob()), + CoroutineScope(SupervisorJob()) ).also { repository = it } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt index ed3a29b6..ffaee45b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/PrivateSettings.kt @@ -2,19 +2,31 @@ package hrln.interactive.sidekit.apps.integrations import android.content.Context import androidx.core.util.AtomicFile -import org.json.JSONObject import java.io.File +import org.json.JSONObject /** Credentials stay in app-private storage, encrypted with a non-exportable Android Keystore key. */ class PrivateSettings(context: Context, name: String) { private val file = AtomicFile(File(context.noBackupFilesDir, "$name.sealed")) private val cipher = KeystoreCipher("sidekit_$name") - @Synchronized fun read(): JSONObject = if (!file.baseFile.exists()) JSONObject() else + + @Synchronized fun read(): JSONObject = if (!file.baseFile.exists()) { + JSONObject() + } else { JSONObject(String(cipher.decrypt(file.readFully()), Charsets.UTF_8)) + } + @Synchronized fun write(value: JSONObject) { val bytes = cipher.encrypt(value.toString().toByteArray(Charsets.UTF_8)) val out = file.startWrite() - try { out.write(bytes); file.finishWrite(out) } catch (e: Exception) { file.failWrite(out); throw e } + try { + out.write(bytes) + file.finishWrite(out) + } catch (e: Exception) { + file.failWrite(out) + throw e + } } + @Synchronized fun clear() = file.delete() } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt index 1e7d2440..dfaad268 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/integrations/SecretCipher.kt @@ -37,7 +37,7 @@ class KeystoreCipher(private val alias: String = "sidekit_banking") : SecretCiph .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) - .build(), + .build() ) return generator.generateKey() } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt index 56f392ee..072e21ca 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicPlayback.kt @@ -38,7 +38,7 @@ data class PlaybackState( val isPlaying: Boolean = false, val positionMs: Long = 0, val anchorAt: Long = 0, - val durationMs: Long = 0, + val durationMs: Long = 0 ) { val track: Track? get() = queue.current @@ -78,7 +78,9 @@ interface MusicPlayer { * * Main thread only. */ -class MusicPlayback private constructor(private val context: Context) : MusicPlayer, MediaSessionOwner { +class MusicPlayback private constructor(private val context: Context) : + MusicPlayer, + MediaSessionOwner { override val sessionId = "music" override val notificationChannelId = "music" override val notificationChannelName = "Music playback" @@ -286,14 +288,17 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla } private fun session(): MediaSession = session ?: MediaSession(context, "SideKit Music").also { s -> - s.setCallback(object : MediaSession.Callback() { - override fun onPlay() = play() - override fun onPause() = pause() - override fun onSkipToNext() = next() - override fun onSkipToPrevious() = previous() - override fun onSeekTo(pos: Long) = seekTo(pos) - override fun onStop() = stop() - }, main) + s.setCallback( + object : MediaSession.Callback() { + override fun onPlay() = play() + override fun onPause() = pause() + override fun onSkipToNext() = next() + override fun onSkipToPrevious() = previous() + override fun onSeekTo(pos: Long) = seekTo(pos) + override fun onStop() = stop() + }, + main + ) s.isActive = true session = s } @@ -312,7 +317,7 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla .putString(MediaMetadata.METADATA_KEY_ALBUM, track.album) .putLong(MediaMetadata.METADATA_KEY_DURATION, s.durationMs.takeIf { it > 0 } ?: track.durationMs) .apply { art?.let { putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, it) } } - .build(), + .build() ) } session.setPlaybackState( @@ -320,10 +325,10 @@ class MusicPlayback private constructor(private val context: Context) : MusicPla .setActions( SessionState.ACTION_PLAY or SessionState.ACTION_PAUSE or SessionState.ACTION_PLAY_PAUSE or SessionState.ACTION_SKIP_TO_NEXT or SessionState.ACTION_SKIP_TO_PREVIOUS or - SessionState.ACTION_SEEK_TO or SessionState.ACTION_STOP, + SessionState.ACTION_SEEK_TO or SessionState.ACTION_STOP ) .setState(if (s.isPlaying) SessionState.STATE_PLAYING else SessionState.STATE_PAUSED, s.positionMs, if (s.isPlaying) 1f else 0f, s.anchorAt) - .build(), + .build() ) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt index f329ad1b..d8246ff4 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/music/MusicService.kt @@ -34,7 +34,7 @@ class MusicService : Service() { manager.createNotificationChannel( NotificationChannel(playback.notificationChannelId, playback.notificationChannelName, NotificationManager.IMPORTANCE_LOW).apply { setShowBadge(false) - }, + } ) } } @@ -66,7 +66,9 @@ class MusicService : Service() { stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() } + state.isPlaying -> startForeground(playback.notificationId, notification(playback, state), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) + else -> { getSystemService(NotificationManager::class.java).notify(playback.notificationId, notification(playback, state)) stopForeground(STOP_FOREGROUND_DETACH) @@ -80,7 +82,7 @@ class MusicService : Service() { this, 0, Intent(this, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), - PendingIntent.FLAG_IMMUTABLE, + PendingIntent.FLAG_IMMUTABLE ) return Notification.Builder(this, playback.notificationChannelId) .setSmallIcon(SidekitIcon.MusicNote2.drawable) @@ -97,8 +99,8 @@ class MusicService : Service() { Notification.Action.Builder( Icon.createWithResource(this, if (state.isPlaying) SidekitIcon.Pause.drawable else SidekitIcon.PlayArrow.drawable), if (state.isPlaying) "Pause" else "Play", - action(ACTION_PLAY_PAUSE), - ).build(), + action(ACTION_PLAY_PAUSE) + ).build() ) .addAction(Notification.Action.Builder(Icon.createWithResource(this, SidekitIcon.SkipNext.drawable), "Next", action(ACTION_NEXT)).build()) .setStyle(Notification.MediaStyle().setMediaSession(playback.sessionToken).setShowActionsInCompactView(0, 1, 2)) @@ -110,7 +112,7 @@ class MusicService : Service() { this, action.hashCode(), Intent(this, MusicService::class.java).setAction(action), - PendingIntent.FLAG_IMMUTABLE, + PendingIntent.FLAG_IMMUTABLE ) companion object { From 73f030baeb74aa24ccca5831b86400e2604ba8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 19:19:36 +0100 Subject: [PATCH 05/46] Cache remote artwork without adding an image library Spotify artwork is HTTPS, while Thumbnails only asks MediaProvider for a local content:// thumbnail, and the catalog has no Coil/Glide/OkHttp. RemoteArtwork does the smaller job: same 1/32-of-heap LRU as Thumbnails, a 16 MB disk cache keyed by url+size and trimmed oldest-first, two fetches at a time with timeouts and an 8 MB cap, cancellation that disconnects the socket when a row leaves composition, and a bounds/inSampleSize decode into an RGB_565 crop so a 104 px tile never holds a full cover. RemoteArtworkImage draws it into a box and shows a Sidekit icon until the image arrives or if it never does. Failures are not cached, so scrolling back retries. --- .../sidekit/apps/media/RemoteArtwork.kt | 250 ++++++++++++++++++ .../sidekit/apps/media/RemoteArtworkTest.kt | 72 +++++ 2 files changed, 322 insertions(+) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/media/RemoteArtworkTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt b/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt new file mode 100644 index 00000000..5d541565 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt @@ -0,0 +1,250 @@ +package hrln.interactive.sidekit.apps.media + +import android.app.ActivityManager +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.util.LruCache +import androidx.compose.foundation.layout.Box +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.core.util.AtomicFile +import hrln.interactive.sidekit.design.components.SidekitIconImage +import hrln.interactive.sidekit.design.icons.SidekitIcon +import java.io.ByteArrayOutputStream +import java.io.File +import java.net.HttpURLConnection +import java.net.URL +import java.security.MessageDigest +import kotlin.math.roundToInt +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.currentCoroutineContext +import kotlinx.coroutines.withContext + +/** + * Artwork that lives on the network, for remote libraries (Spotify, podcast feeds). + * + * Local media keeps using [Thumbnails], which asks MediaProvider for a cached thumbnail. There is + * no provider here, so this object does the same job with a bounded HTTP fetch: + * + * - memory: the same 1/32-of-heap LRU as [Thumbnails], shared across sizes; + * - disk: `cacheDir/artwork`, one file per url+size, trimmed to [MAX_DISK_BYTES] oldest-first, so + * scrolling the same list after a restart does not refetch; + * - network: at most two fetches at once, 10 s timeouts, an 8 MB cap, and cancellation that + * disconnects the connection when the row leaves composition; + * - decode: bounds first, `inSampleSize` to roughly the drawn size, then [Thumbnails.cropTo] in + * RGB_565, so a 104 px tile never holds a full-resolution cover. + * + * Failures return null and are not cached, so a later scroll retries. + */ +object RemoteArtwork { + internal const val MAX_DISK_BYTES = 16L * 1024 * 1024 + private const val MAX_DOWNLOAD_BYTES = 8 * 1024 * 1024 + private const val TIMEOUT_MS = 10_000 + + @Volatile private var memory: LruCache? = null + + @OptIn(ExperimentalCoroutinesApi::class) + private val fetch = Dispatchers.IO.limitedParallelism(2) + + private fun memory(context: Context): LruCache = memory ?: synchronized(this) { + memory ?: run { + val memoryClassMb = context.getSystemService(ActivityManager::class.java)?.memoryClass ?: 128 + object : LruCache(memoryClassMb * 1024 * 1024 / 32) { + override fun sizeOf(key: String, value: Bitmap) = value.allocationByteCount + }.also { memory = it } + } + } + + private fun key(url: String, w: Int, h: Int) = "$url@${w}x$h" + + private fun cacheDir(context: Context) = File(context.cacheDir, "artwork").apply { mkdirs() } + + /** Stable file name for a url+size. Hashed so URLs with query strings stay filesystem-safe. */ + internal fun fileName(url: String, w: Int, h: Int): String { + val digest = MessageDigest.getInstance("SHA-256").digest(key(url, w, h).toByteArray(Charsets.UTF_8)) + return digest.joinToString("") { "%02x".format(it) } + ".img" + } + + fun cached(context: Context, url: String, w: Int, h: Int): Bitmap? = memory(context)[key(url, w, h)] + + /** Loads [url] cropped to [w] x [h] px. Null while offline, on a bad response, or undecodable. */ + suspend fun load(context: Context, url: String, w: Int, h: Int): Bitmap? { + if (w <= 0 || h <= 0 || url.isBlank()) return null + val cache = memory(context) + val key = key(url, w, h) + cache[key]?.let { return it } + return withContext(fetch) { + cache[key]?.let { return@withContext it } + val bitmap = readDisk(context, url, w, h) ?: download(context, url, w, h) ?: return@withContext null + cache.put(key, bitmap) + bitmap + } + } + + /** Drops the in-memory bitmaps; the disk cache stays until evicted or [clear]. */ + fun clearMemory() { + memory?.evictAll() + } + + /** Removes memory and disk copies (sign-out, "clear cache"). */ + fun clear(context: Context) { + clearMemory() + cacheDir(context).deleteRecursively() + } + + private fun readDisk(context: Context, url: String, w: Int, h: Int): Bitmap? = try { + BitmapFactory.decodeFile(File(cacheDir(context), fileName(url, w, h)).absolutePath) + } catch (_: Exception) { + null + } + + private suspend fun download(context: Context, url: String, w: Int, h: Int): Bitmap? { + val bytes = get(url) ?: return null + val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bounds) + if (bounds.outWidth <= 0 || bounds.outHeight <= 0) return null + val options = BitmapFactory.Options().apply { + inSampleSize = sampleSize(bounds.outWidth, bounds.outHeight, w, h) + } + val decoded = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options) ?: return null + // cropTo recycles the source; store the cropped result so the next restart decodes it directly. + val bitmap = Thumbnails.cropTo(decoded, w, h) + writeDisk(context, url, w, h, bitmap) + return bitmap + } + + private suspend fun get(url: String): ByteArray? { + val connection = try { + (URL(url).openConnection() as HttpURLConnection).apply { + connectTimeout = TIMEOUT_MS + readTimeout = TIMEOUT_MS + instanceFollowRedirects = true + setRequestProperty("Accept", "image/*") + } + } catch (_: Exception) { + return null + } + // A row that leaves composition cancels this coroutine; drop the socket rather than + // finishing a download nobody wants. + val cancel = currentCoroutineContext()[Job]?.invokeOnCompletion { connection.disconnect() } + return try { + if (connection.responseCode !in 200..299) return null + val declared = connection.contentLengthLong + if (declared > MAX_DOWNLOAD_BYTES) return null + connection.inputStream.use { input -> + val out = ByteArrayOutputStream(declared.takeIf { it in 1..MAX_DOWNLOAD_BYTES }?.toInt() ?: 8192) + val buffer = ByteArray(16 * 1024) + var total = 0 + while (true) { + val read = input.read(buffer) + if (read < 0) break + total += read + if (total > MAX_DOWNLOAD_BYTES) return null + out.write(buffer, 0, read) + } + out.toByteArray() + } + } catch (_: Exception) { + null + } finally { + cancel?.dispose() + connection.disconnect() + } + } + + private fun writeDisk(context: Context, url: String, w: Int, h: Int, bitmap: Bitmap) { + val file = File(cacheDir(context), fileName(url, w, h)) + val atomic = AtomicFile(file) + try { + val out = atomic.startWrite() + if (!bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)) { + atomic.failWrite(out) + return + } + atomic.finishWrite(out) + } catch (_: Exception) { + atomic.failWrite(null) + return + } + trim(cacheDir(context)) + } + + /** Keeps the disk cache under [MAX_DISK_BYTES], dropping least-recently-used files first. */ + internal fun trim(dir: File, maxBytes: Long = MAX_DISK_BYTES) { + val files = dir.listFiles()?.sortedBy { it.lastModified() } ?: return + var total = files.sumOf { it.length() } + for (file in files) { + if (total <= maxBytes) break + total -= file.length() + file.delete() + } + } + + /** Power of two that keeps the decode near [w] x [h] instead of full resolution. */ + internal fun sampleSize(width: Int, height: Int, w: Int, h: Int): Int { + var sample = 1 + while (width / (sample * 2) >= w && height / (sample * 2) >= h) sample *= 2 + return sample + } +} + +/** + * Draws [url] filling this box, with a Sidekit icon until it arrives (or if it never does). + * Requests cancel when the composable leaves composition, and a hit in the memory cache paints + * without a recomposition of the row. + */ +@Composable +fun RemoteArtworkImage( + url: String?, + width: Dp, + height: Dp, + modifier: Modifier = Modifier, + placeholder: SidekitIcon = SidekitIcon.Album, + contentDescription: String? = null +) { + val context = LocalContext.current.applicationContext + val density = LocalDensity.current + val w = with(density) { width.roundToPx() } + val h = with(density) { height.roundToPx() } + val inspection = LocalInspectionMode.current + var image by remember(url, w, h) { + mutableStateOf(url?.let { RemoteArtwork.cached(context, it, w, h)?.asImageBitmap() }) + } + if (url != null && image == null && !inspection) { + LaunchedEffect(url, w, h) { + image = RemoteArtwork.load(context, url, w, h)?.asImageBitmap() + } + } + Box(modifier.drawBehind { image?.let { drawRemoteArtwork(it) } }) { + if (image == null) { + SidekitIconImage( + icon = placeholder, + modifier = Modifier.align(Alignment.Center), + contentDescription = contentDescription + ) + } + } +} + +private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawRemoteArtwork(image: ImageBitmap) { + val dst = IntSize(size.width.roundToInt(), size.height.roundToInt()) + drawImage(image, srcOffset = IntOffset.Zero, srcSize = IntSize(image.width, image.height), dstSize = dst) +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/media/RemoteArtworkTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/media/RemoteArtworkTest.kt new file mode 100644 index 00000000..746228e4 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/media/RemoteArtworkTest.kt @@ -0,0 +1,72 @@ +package hrln.interactive.sidekit.apps.media + +import java.io.File +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class RemoteArtworkTest { + @get:Rule val temp = TemporaryFolder() + + @Test + fun `file name is stable for the same url and size`() { + val url = "https://i.scdn.co/image/ab67616d00001e02000000000000000000000000" + assertEquals(RemoteArtwork.fileName(url, 104, 104), RemoteArtwork.fileName(url, 104, 104)) + } + + @Test + fun `file name separates sizes and urls`() { + val url = "https://i.scdn.co/image/abc" + assertNotEquals(RemoteArtwork.fileName(url, 104, 104), RemoteArtwork.fileName(url, 208, 208)) + assertNotEquals(RemoteArtwork.fileName(url, 104, 104), RemoteArtwork.fileName("$url?v=2", 104, 104)) + } + + @Test + fun `file name is filesystem safe`() { + val name = RemoteArtwork.fileName("https://example.com/a b?x=1&y=2#frag", 48, 48) + assertTrue(name, name.matches(Regex("[0-9a-f]{64}\\.img"))) + } + + @Test + fun `sample size keeps the decode near the drawn size`() { + assertEquals(1, RemoteArtwork.sampleSize(300, 300, 300, 300)) + assertEquals(4, RemoteArtwork.sampleSize(640, 640, 104, 104)) + assertEquals(16, RemoteArtwork.sampleSize(2560, 2560, 104, 104)) + assertEquals(1, RemoteArtwork.sampleSize(10, 10, 104, 104)) + } + + @Test + fun `trim drops least recently used files until under the cap`() { + val dir = temp.newFolder("artwork") + val files = (0 until 5).map { i -> + File(dir, "file$i.img").apply { + writeBytes(ByteArray(100)) + setLastModified(1_000L + i) + } + } + + RemoteArtwork.trim(dir, maxBytes = 250) + + assertEquals(2, dir.listFiles()!!.size) + assertTrue(!files[0].exists() && !files[1].exists() && !files[2].exists()) + assertTrue(files[3].exists() && files[4].exists()) + } + + @Test + fun `trim leaves a cache under the cap alone`() { + val dir = temp.newFolder("artwork") + File(dir, "one.img").apply { writeBytes(ByteArray(10)) } + + RemoteArtwork.trim(dir, maxBytes = 100) + + assertTrue(File(dir, "one.img").exists()) + } + + @Test + fun `trim tolerates a missing directory`() { + RemoteArtwork.trim(File(temp.root, "nope"), maxBytes = 100) + } +} From 432f986b94e73a8b99937b5f0f2871657aedf5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 20:27:10 +0100 Subject: [PATCH 06/46] Add the Sidetracks UI mockup --- app/screenshots/sidetracks_album.png | Bin 0 -> 42077 bytes app/screenshots/sidetracks_artist.png | Bin 0 -> 50204 bytes app/screenshots/sidetracks_home.png | Bin 0 -> 53941 bytes app/screenshots/sidetracks_library_albums.png | Bin 0 -> 53676 bytes app/screenshots/sidetracks_now_playing.png | Bin 0 -> 47123 bytes app/screenshots/sidetracks_queue.png | Bin 0 -> 50727 bytes app/screenshots/sidetracks_search.png | Bin 0 -> 45126 bytes app/screenshots/sidetracks_settings.png | Bin 0 -> 34654 bytes app/screenshots/sidetracks_sign_in.png | Bin 0 -> 33859 bytes .../apps/spotify/MockSpotifyDataSource.kt | 226 ++++++++++ .../sidekit/apps/spotify/SpotifyComponents.kt | 402 ++++++++++++++++++ .../sidekit/apps/spotify/SpotifyGraph.kt | 244 +++++++++++ .../apps/spotify/SpotifyLibraryScreens.kt | 306 +++++++++++++ .../sidekit/apps/spotify/SpotifyModels.kt | 153 +++++++ .../apps/spotify/SpotifyNowPlayingScreens.kt | 248 +++++++++++ .../apps/spotify/SpotifySearchScreen.kt | 144 +++++++ .../apps/spotify/SpotifySettingsScreens.kt | 220 ++++++++++ .../sidekit/apps/spotify/SpotifyViewModel.kt | 301 +++++++++++++ .../sidekit/navigation/SidekitNavHost.kt | 2 + .../interactive/sidekit/navigation/SubApp.kt | 1 + .../res/drawable-nodpi/cover_aurora_vale.png | Bin 0 -> 16112 bytes .../res/drawable-nodpi/cover_coastline.png | Bin 0 -> 21560 bytes .../drawable-nodpi/cover_focus_no_words.png | Bin 0 -> 10594 bytes .../res/drawable-nodpi/cover_glass_hours.png | Bin 0 -> 21729 bytes .../res/drawable-nodpi/cover_golden_hour.png | Bin 0 -> 13325 bytes .../main/res/drawable-nodpi/cover_ivy_ash.png | Bin 0 -> 20817 bytes .../drawable-nodpi/cover_kitchen_radio.png | Bin 0 -> 10167 bytes .../drawable-nodpi/cover_late_night_drive.png | Bin 0 -> 21430 bytes .../drawable-nodpi/cover_long_way_home.png | Bin 0 -> 17157 bytes .../res/drawable-nodpi/cover_lumen_park.png | Bin 0 -> 9983 bytes .../res/drawable-nodpi/cover_mira_sol.png | Bin 0 -> 12819 bytes .../drawable-nodpi/cover_night_signals.png | Bin 0 -> 20824 bytes .../drawable-nodpi/cover_rooms_we_left.png | Bin 0 -> 9224 bytes .../res/drawable-nodpi/cover_small_hours.png | Bin 0 -> 21837 bytes .../res/drawable-nodpi/cover_soft_focus.png | Bin 0 -> 19114 bytes .../res/drawable-nodpi/cover_static_coast.png | Bin 0 -> 9048 bytes .../drawable-nodpi/cover_sunday_morning.png | Bin 0 -> 14411 bytes .../apps/spotify/SidetracksScreenshotTest.kt | 170 ++++++++ 38 files changed, 2417 insertions(+) create mode 100644 app/screenshots/sidetracks_album.png create mode 100644 app/screenshots/sidetracks_artist.png create mode 100644 app/screenshots/sidetracks_home.png create mode 100644 app/screenshots/sidetracks_library_albums.png create mode 100644 app/screenshots/sidetracks_now_playing.png create mode 100644 app/screenshots/sidetracks_queue.png create mode 100644 app/screenshots/sidetracks_search.png create mode 100644 app/screenshots/sidetracks_settings.png create mode 100644 app/screenshots/sidetracks_sign_in.png create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt create mode 100644 app/src/main/res/drawable-nodpi/cover_aurora_vale.png create mode 100644 app/src/main/res/drawable-nodpi/cover_coastline.png create mode 100644 app/src/main/res/drawable-nodpi/cover_focus_no_words.png create mode 100644 app/src/main/res/drawable-nodpi/cover_glass_hours.png create mode 100644 app/src/main/res/drawable-nodpi/cover_golden_hour.png create mode 100644 app/src/main/res/drawable-nodpi/cover_ivy_ash.png create mode 100644 app/src/main/res/drawable-nodpi/cover_kitchen_radio.png create mode 100644 app/src/main/res/drawable-nodpi/cover_late_night_drive.png create mode 100644 app/src/main/res/drawable-nodpi/cover_long_way_home.png create mode 100644 app/src/main/res/drawable-nodpi/cover_lumen_park.png create mode 100644 app/src/main/res/drawable-nodpi/cover_mira_sol.png create mode 100644 app/src/main/res/drawable-nodpi/cover_night_signals.png create mode 100644 app/src/main/res/drawable-nodpi/cover_rooms_we_left.png create mode 100644 app/src/main/res/drawable-nodpi/cover_small_hours.png create mode 100644 app/src/main/res/drawable-nodpi/cover_soft_focus.png create mode 100644 app/src/main/res/drawable-nodpi/cover_static_coast.png create mode 100644 app/src/main/res/drawable-nodpi/cover_sunday_morning.png create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt diff --git a/app/screenshots/sidetracks_album.png b/app/screenshots/sidetracks_album.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc8097350b7a00d0eefa4928282f38217e7c149 GIT binary patch literal 42077 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 ZuRl(UFIm(1DfqZS26{#}K3}tc@ju)w1)%@{ literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_artist.png b/app/screenshots/sidetracks_artist.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7f5c94a42a88a18f1cc8d3435b6009a955fa8a GIT binary patch literal 50204 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_home.png b/app/screenshots/sidetracks_home.png new file mode 100644 index 0000000000000000000000000000000000000000..bdeea4ab7952c3453637e715534664648f1d3408 GIT binary patch literal 53941 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 T+B4R~00000NkvXXu0mjfaK6}I literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_library_albums.png b/app/screenshots/sidetracks_library_albums.png new file mode 100644 index 0000000000000000000000000000000000000000..1bd58c714423e3582b40a8b8a6ef533fcbc8099d GIT binary patch literal 53676 zcmdq|RZv}B6D|tl?(QxL?iQR7g1fuBLvVKq8eBtgx8SmX;2PXDcyRZBt~X!p+UN4W zJ9Vz8nsYH`kM15lx}SbJN=ZQq6^RH50s;b6`twH>2nfhc2neV^1UTTy$Ds<~K_G=|gTI{~p16aZZ*MQhPqHK)a`;IJp`i()r9q)E3=|mRk#x}qou<(}v|sd`S?jQy zf5n*&I&X8)fARJ(8H}58^lokCJtoa8!y?8M+KpK39gD|AG> zasNp?+sp67)=>4sm%p_m$?3aqx6R$|eYJdlr9>VRl`~Z)Mcz76@biTgMP3>oU(N#Z z64pEXZz>F1S>eON!Zco6+?N`yRmE3**00DaR?XBjH7mMbZ)yx)@eMqCWF!zMsZo)f z0$Yk@uBIf=%L3k>HW0V`@uvlE7fm%X#C9dF1q1~2dcLLQK^T5nGCx8OxDSyd@-*(h ztJH3&blINYg%`OG!jHfYQWJM{+4;&#M9b)YSd^w?jX-HuC5z1r{rqZ3%Z@6<8!&ZGNDzgh3ErvL4d zCH5t$koRmG{`2P;!CIPt=N)qTUpv7FrY$=2)C&4+T=oXaA94IjSXVitJ+Z}yoWh{8 z!y(=PA9B^#3fDC7Dx$Txxw$zaJ@b?b4vXEuH}tJjZ(<_1|8=NGaH-#5AurxGJ*{%) z;p*D(xI_MSu-kX@de?3Ev$0WY*Kxf=`{fV4i1g>DBhBt*nKTD|H@~qoNQ9qEEAg$a z`{e;I$A$*N*I9C4O=2QB5kbWsTZ%ko*x1@OOlRr^yxbsCJ@=8nPI<6@Vyk={)nBl!2Zui-E&y?mBNSuc&C4{#>}by0PI{1f2Us1Xw;) z$netM@^6RFpkSISfiHj4Q$D5U*-Q|m$8;M-$uh3S)8~5jj)w54si|pM11s(Qio!

Io9JVs@oX0g#C4kz+$mNc0Y_ec64}3Cs@mARO?x#0=JUelCvEO>6Evu{SW#T3TCM(;ylL96*DW zd2g-{MH{f%=O8E&b?#b$;X8i|Po9v&XJ)cDaFw8KEkJ@pEWR>q_{BwYthBx;cI zaWKa?qZI;iUhs22tN&I}3`e;rpnXW-c=oW*h;o_vGC0YkdQ};A3)lMU`MZCH4NtDi zyUjWCEqUC=?>?A7E?F+gMumi1+$*ffd%Z5v5M1o^X?DiPZ4uA@ry`7C)8d{2O2Ry( zzYE-~+4;C?!YthaFhTZ8mstbGHFk(>c!n7ZwOzS*UPl7iatb2Ga3m< zG)g{aSb}}JEd0vu{7dJDIWef7-r#CAe1)Rk#Psy6{zLt}6lTckvNCBtolC&{j^fXM zhLf>x+2}tM0#N{LB#dBe4}`~~iW;8NgbYfn%HY(6ytijd4;I|rleKou@eEFS%J@~f zZ_JQzd!vwhxs+`Jr_qen=i6*@aq-$i&!HF`)88Q?Fi1F273|B)%j>6UpbgPEYFc7y zYg>*K10Gey$^P=@^1BwN%+sVn#j?_8EbOKkp7#6gapqn08t065gkreRiGnx7AwLT% z!P<3Skj6X4L(?sg{&%&Nld^MT-9*Hp?XWo1cL~R+(&ZJ1*rzE!;){Q==j?4@x+i%d zQvQ((gd!1lGRsu--Nx3)q&VNDY*_aSt^1kw(h7<)G#2L_h!RGsxQFdJ9F@4z&0p|lRu$2!j!LIEarbGi=OY@e zX^UoYqR}GJe6yFiZeV*`8&;FJan`J6_oMn`JS+3CAh46-e>*{C`RgculL+5&yI6o6O;Z+bj z?d`IuOw!0#Lw#())H4SSIduWJjNV111-^Dq>>>^DTl`L&f$B|$5B}~6o3NsW?}#f3 zUhs^o3_5lEPF?djWkmdT#RD%S-*qz{1fq!}@T(FuL6PixpZCCba`Yj@Wxy3MOsWk? z{c(Q8!6Vv&hS?j$zs7Z=`N2eBg*=uG;@z4IKP+$sbTRE%W?%dh(DVb2-eT zZ`Mrovn%&otX+939-%gcl+6yZ#;OJ6sJ!+O_#r zXBHTOjx)bA*>~7}(@RU%Mbx1QoQU89b%AXZ*sr+PI(Yf`xX^v-K$x^E-8)Mespbya zhjeT|;o-IKf+5&Y;9=F;=RlX(u z$<@mk8-IUXJM-U1Jm;JgGzCXSPE6pZ43`o(#JmoT(-^>h5n$8JP)Rr+fH=O0z2PRj z`xyufxWKOn_)b;GzqlMsQQiQz%}lLdiY@Bse|{kjNk?e9(dCC2Y-_;Ud7bp1mCfFP zXiQ-c&(pfDX}SF#-57;{oee7mN)NEX5)7J{AUvk(I@C>!fD^KRSsB#-A6XfSE#&FJ zfl~8^fBPtqme}>n5)l)3UhfQil_gX_01}iKV`dPaqZabMee+szbWBVY7oC_mG0y+q zF@hu|C6DHH9o;3b!igQ6^nGDOi#ZXomh+v!8KEwUE@b$kI9pBI5yMZDex$oQAn~Yl z`+u&GX5d}Mm~OjJtv*vMe+ARn(XlF1@qaBL3mHCS(~}K^2Vyu`f>$5{Yaep|UoyN0 zy}Z0sYZ(CPkHKOlx9ut$LY-U@qDZKn%j(gb-U~S1W55{_?g4N^?Xx| zcN{pvzE{2RVP=s5a}@?f4jmVt+@J5A=a!Z-loWJzvjklJL<)Fa79x-2aNDN3i=Y~) zG&ycmS9d+yxL=L)R@l~bYMd-JNO5y??I1@#4ej~sW!TIZclyf01mE0uimii+{>lU-fH zj5&zm=jLR93YRPw5uNPqtLVwR@3nX6#{kt?hue-~Bezo=5u7^M+q>g?9&%mnPok?= z)1>@MjFMzolwJ46ocjr?y*Ad`J=$3+wf|I9R=VAZ8+z@gIPi6KelQ=wsj<5%`o)Bw zTEl{z?^j<9xKI6PEw+(Xu+BU4)uXwJH9oCGe0{e>TymOk7JT8^73JlX9Y3VvZ((ro zT$En$B`hJ;(jS8q{)iF{V`g=%9l-t$b_|-VMRN%`I%5^sHJ0_;a?I64v zAWQW!Zb9!2hH2{RTF+VGIR?@j8eZUJ4`7tzQP~M4U5M(&5EQ34gEga=E4VabS)jPF z+~!{42*^l6QyqiH?6PxlNx8$N#TCvKflhwgTdRd^tmPT@Gz1(w@yo$lEF&!sjBlf? z-K)_vR<@WD!zw5tp(+ukR@$lbzr`(cg-F%F+J)bpgNmIS2BS73gg9g4@YrAF%w=DW+Oxm zHxGCBR^5!T69pG_ULfZD<+Ot#2!^i%HIe+il84hkqGraE!^OezG>3`GAqo=Tv*-}sgbHVNqa*;i-*GePjy*qLAm=s(h8^NOUh=pRKPGYN&l0-LHE-tRkKrG>@ zzhxt~^MPSyda5}VASRiFL7?+$0bb(m?H5L21XoXWbVhh!{W#Uyf?U|D-z$aBplxOn z@1TuVETeo)d-xy@>lZBX&!>c!P?Zr5FSj?vP*NYLSeXRx!CH#gcqYPX67FFL_w04~ zc9*a@Dv2Sc)y_wT)qUgZEDS?nWK4ucC-z~AY!(?$$bhbe9H^Tw`x5ZMg`XMC|Kj%l zd#Y(RGg!~Ri*@CtrJv<9I3(3IG|CA%ElM9Bz2|0U3w3mL)pd1=DMK-~ZF>JMHRyj0 z)DY#&C!NBuikeOP-kX5685|7Nykjw*Y3|Mj{45D=3dahW>(h~f@Nhc>K0bcCBNGsy z>IA-m4+_l`kri5{X_-_jog*QJ>UpY|6t@V_DFi%n6LDYjDWLkU_>)66X{TDil3 zU!b#nM72-yPO*2P17XuEsG*{wY5)muXu@|0JZG_rLVxp^Y=raKGS#^budB)dz~Yn= zh*r2LZoehVo0{frD1gN{z6E^|>np4_PpU*bZ*ksf8Hxt3?^;-*)N#LG z+L%Ui7FV?>zyf_asi~=z%u^#R5NT;?u3c%suOcBc;p~&W@zY|FoQ#br`~Q0ey_|a3 z_kn{Blzz2a%LxdTh{T1aLx01n&p`#!dOrZG2^_jl0#j{rRdI35pdX*Us<^wmYtVw_ zPekBSv@sU=g|~ThWK-b+{BY=Vae4X3bYJN&EF-4z)D$Y<*#ykCgJLu-NN^5Q-elsziESXxg*r%M(1NL(Xq?5@(kuZZ~ zVPtG@APeiQ*WvCe*U#Z@8z`tOT0okSJJE9p%aF^Rvkx;*&RmCF@atd9sMh^iYAB!m z+qwu^(p=&#IhIkQQfcFHzIhg?pYU(yo4e+s>C24Cv0V04R8&k3%^9eWo#f@ey6@Dg zsd1MMH{e%j#%7f9*ojm}Gkj1pUo-h>Jm~y$2b5y-Ni|QW>okx!4;9H`LbGAPzPVJV zWm$$ee0i5x&*QSPzkJaPveckaC+EZWbs6|#xzVxciy8nakuel`dzhR{qLr=W66*l! z+}q3TX2QE--p=8er+tyPGL0%7^WAt^L&H3e_(}iU1`Xe?*6V56Ie6i7$eR%IXVI5a zk8aneEuieJc-(k2%wYarE?dL(N^|^Fv?~v$8^Q{l`RxR!!4MyLjiB#H9D3LP^cf-d+wL zqjrJj{B!81BrRe|!;dkbfmqHh52K5Z! z3_i|FSn=Nt>rwRP5G_W5CnO(c4S+TS>0(r8J`>-Ox4k6Br@U#nU6iPknv$ zzX5c%VbBSb2?4Dy5VH67jy_r5kB235;N<(Jx%*{YHiXhbYqY*n?|Ms#Y9>HYJp2PJ zENrR+9d#1K&H=Mm9~&2UbLsH7&=Ky=F~=wCV--deIqO5Ew~dGX#Khh7*t~V)FzM(l zZxXzU%L&}_Hp?p98f%9P+Z#okLWrHwc_p^VQL}Pd3Kqi-(8q;RFrG%N>nbi>kJ^^`Qj^6{1sHMc!yJcWDFt?32nCLMqD4 zw=r##u%I^eEbM32b|aThiFeTQ#9wrbhu@VR*Y`p^ir*^Y$U(jWu5%GxWqIf9^$=FH?wA~X%aV541Z$w>yrZ`e@04Lcb*BH*^DC}}O1nui zk?!27PR~qQWy5yQ!M3Mi90%1y`iJtubWQ7W@0V-5eQ%1Sd6C7PO>o3=6C-V5uy9MidB zvsh}-qlZ`@72|7m@R&9Ts8HPZ8E$TV*7%LfI3~#j_6f@+qUqYb9(TWv<{Uo**w$$^ zR(K$(fZLG#)vT(bm-)#`Gb?3H2fP6l7>@FoyAXN3K)=b_cplJq_5~^e-8;Az$8tui z#BYBlKAc@&cD%iuHE+L&=Jn~o89s00V{bn%?@JXwo@3|ThZ2Y5M@<4X`}ZfM;LD4% z0|2qCdz{I8{nIu3-H*Tp4F8eybz&BG5)=nq|CU-i(SE0zhWaLfE`+1SL)n+|ZY5D1 z*-26|%>74gHZ)`|7bB15*PL<#p7PnWi@64@XB{Wii_4gi4nbT}06Ug<6Dm(b(Q==W z%k0!j89PzM)qgqa7#`Pn=1<-x-Lghv+oQd}c;00VI1Cdy2SIC)$?HL`w|mrBxYGx!<1yRP!r%TK@p+vEU1`2mwGpYt27b5j)d z^N=uw)w}X4j%OOJo7Ei;70y=jCs=Lkdd=TH_TX#+HAky)^SXBheySY{XB>p%a^vvN z+UpxY$kft z^ngX?o!0$aS52Q|QD3i zPk7Xqn^s?h%E|_#(_XM)RRhQ2ImrSay|=>fD}5>AAWYM>3_Ta0qDU$ot-r!X!Lw+c z`d7p`w(jE9kRZ&1y9`_urzC7Ghs9Y~M<`pt7^cd^O&(%}=D8H%s^li4I0g_-lxqOa zZkiH=57+VT5SdlP!ngi*-Kh`NfFxAwG|B*+(3fAXICcwB<|b^(e1ql}M`r8tdFP0e z>Pn$zeaWHKik6(U(y@{G8`pi^k-6Y&g@z7QR&+s|g+wU)sLw;X%dtOQsqR|liH0Bd z&M{tj*_v&o6U7rfk{A?91-?djmqXWs1*2mdRP56MvaWj)>Re`o@}>+O85omKF@$E6 zHpIXNoRJc=34otR3*E0jJw&d0v)?_E1lu+B$8XI>oF5-klRreJXu1NO1@FULuf2@W z7PAv>MOb7VW{2D12;pl=MnL?}-yC5g@HX9=DS}*O_6qw)DS}JV`5Km<^m!b+G6ez> z_ZPO8>TD}$tCi0`-mA5$)_O0OhQPR!a96FB^S0!hB=yCh?(=jRlaXA>M9Zr=D zYh%v?xpro7jZhMgMkiG`g8Ho{q~W8a7=DMEm)C$1_h|SGETcebA#$%jm^Z*}nioWG zLP-K|55w$0q~Ow zl>eIgPPiZ8)gqbTX@sD$$I0(|Uz^5b73M+*RH-q+r`>P<9urtGkxl}s9aEq91%*hS z*bq>ZO%sV;Q4f)?dMz@|PS#qSf3$O>;fJ@e^Rhn0BqVpOSP(CvG1v^X)DmJCW z`vQgRcCnyfXBu~t{PjG-fiK*8{G7r_`iSwf;2$BHz4Jof#3?h`Kw1nlyNn(x+AHrf zpN*^p7VW2S8}i`sEZ&MiGM}-ZPV7)d3g+pWIFN7ziR}9g;GR}{%&5`VC(>Qu))G6i z#AwCbd|!pA#zI)z%ayFVSCJN9@i{-=A?~i-q=)M4t|LcxcEmQm!Woy7ner?2h@Srq z`=eL=T1)z{$?Q~$h*i%>pN#eZ^L^s zpo&fA?Yj87j=_DqYVtiPPIkWQ`(i}oU^jj1fvh0{Z?m5yu2CSq=xU!M4rEN=tn}Pk>&e8v z@oTi&!SgvD336B0tsnsbC5#OUg-x@J^~D&%)mae-a(+cRJo5%mGw?V3JL3BjS5zqX z1$+Q%p|{;oI+oDt+)I6D=hxd!h(Hy-;Vg<2J~IAPL=7VHbw+aS1ZP2Oo!!M0Z$Rth zvjDpMkdp+UL4M55t6GZpFqU8Qju6iCZB?e1t;2#+7IK{up@{JHt2+!C%zQwNMaNZa zfQk?pL6I}?Oq-3gDDtN1%7)4?e8g92mt;w1+ux%YL3=y|uBF1bdoi)CE$;zq>X3_D z-P{2E8hRxNcKpg3J}9Q1p`H?v7i(6L@M3c!@o>g}8d5|Z)W&8|-})=I@C(;yu*#hb z&cd?^rsa6^R1A{mRK3-yPMtN1v9>^d=8>o6RFbp35BEk!7|(#a6LC_CjKE2qTbWerLVMa@teSPFt2!;P;jY_cCxbAV z3bDX)LX2ayV~~REQ|bLcxiZ&rm4Dm@<;x%kgq{Kfwuz-VSI_k3U*!gVl9)zUVsY>m z^+J`!DPI|h|7mNGpjHK6*VT*&F}(Z+QIFsg_Z7{5mb8D_*$CSRq9D4WrHzKz$+pw0 zy-!9xJ7)E!B%KVSqlPWZ6Z`Wiu*ALaPi54M9vrOad9#IyM8$EFT^K|j8X()`HO&2W zC(FBvpGUh9cA8h!64ko1UDL?mL;4O1#i+-qF2R535YQQP=piz?W_K9p-MUYW2dkgC zgdo>rwsGZhinDQ#6v}m2>k!aG9Fh6aLpXD96x1brl`%>4tHfqQ=}5vgQnz|P?6x*ExDX^mZE#DE|xp}QB*U?)CgvI-P1R!;CT?!kdU>KlMSj$lhN zBAW`jirLk3D8EH55*7p+`@t=}41*EGO8ABS*7_~}uUC0K)_~Eva$@m`{ER>A-@QLI z^&cg||Fu4*IOO!{U$8lPK$O$15jCH>t_8P_>M!^m^3555 zLy1)21=}~nq9_Is#aNoJ6I4yAxeT1kKQ48X&M4afdhYF29ib?eh5m4f#E`jN6hYRE%{2Fu znQA#8MkCl5Hl?Mttj)8|?MUixY2|4-hGtP#BuaE_XYT%9IqG>7Q^n!TPgu)Zg54WW z%)pg$h`2P(gVyg#ox9oq| zcu-gnc$%y6-;JgO%~OUNf7ht5p$)SKyTK|typvHL$5GJjHgBc+ORMaE;jq=guB78*~m zhHEc=v|Ee_HG|7;3qQp0)%rMKjto8~9|KuS53!i8{ri?t`(Sr(${YQ@<5t5qcyF1= z%FT$S(VszIq!|;Te9d}ohyh@cKDI`O{d8@ib;l(U3DQahpuHxw7RK$d?p1=z`ui%a z_RD!9G+_W;uP_abNP4@7%gvJPzvDoR@X4(2GreQ(Ks4?nZ8=orH zL~kdZK&$A{xFCO@5A6F^ENzKbW0nGcB>>Pgm07nVKGujg4VJPwe@c#8`SMO??pcCr8y5{^`$g^V+dDDCgpMdPh$b1p*K9D zk!%XK$<`cU;0Q|v!pKKH#(!$FRIGNc`Yh7ydFsp9F^1VvKOw2n@ge+o-) z{Ggr?(R%WI&Y`Y>z$rg1=iIkbb%PJGc<3~Zy1qgV3iXQQ0GNY z%puU2`|E!tfTR^*dqB-m7fE$3 zI${*Og2L!**bJ2UgY*Bqd>%{6GxfgBGHtfJLDm*}ZM#)pxsgIdW`|LjqtDjJD*(7+<;o$Q-z*q9 zrjX-_DY`j$$M@(#^zV;bF;Zc8uvdGJXzjvVY`Q4sMqIz&TD=I++en7K`E8MwLj6E)c>nb6AC zQ)_qdnps`eq^qtv`5Nurj*%)lliT1c!D>q)9TNhzkon&O?3VxhUIE6F1g6EjmDSSp zo(Bql+^(({;$w@6b6)vTjDbbDAS^R7)^oCs;#15AimuPl7xqfnnF4zF_KezXOE#h$EogZ)lW@_g``X|&bQmj|7=640H3jdV~|$%g^c+cV`&;! zx9W?H8Cb1^{GWk}pBGBdu}F3o#B$a=%&$s}?Ifuc3d`Qwu!1xb>u941TXi+}FehJ~ zuV8iBI31|Wsk`dgT|a`yEPX!8mEIoyp~<-#pKb*n39shPe-x}w4j1r_9bf<$d9Vf6 z7VCL4dqf*A?5eG@)=VyU!#*qnr-|9^SEFXm2m2=65;2nLZ!CHy{rXy*gGe7HF~1PP z$!IwIT(+WmqG!qCv3Kb{hXkWkjJY)MgAxU2PCS2uVUa8E%{Fy^48<6;5aPpL%*pc4QK3WG;ycra%iW`c-#?DPP4y=- z`oJ7M=iWPNTz|6yG&-Q$tGS@(`t{CZ(<5EF4Xhn{D8aT)5!!um2v`hx!qn)bhnT8b zzWfg4VqV=Xx_wP1c^||rm@^SIs*My4PT7= z0oG9HhcBnA*8Nh(e28oY_iTB^$Bg_yUuML$^6qr)_*FN>i<^(E_Q#LDk&@w$q%`p8 zWODBSUc|KIzZ4^G4iV?tuCdq1@6ov@mS00liz0VljKps{Daf~$Q{Vy4~_%Oe8L@M!FRCnI$@S<%mF6?*j`vC$P5FR*wTUuE;&UFZNq&x;n-#BeA zF}foP_9sILNV-F+f6;TFD5mWAjxJm*lrj}E*7m5jw}7qOp_)N^3VY}voQE;Ku+~pG zIKbpM7$!hDeoaBd?ClpMI-(#`b^EEnSx>kzUq&2_)E%{fMK7A6xM0G5N{w$3E`G`u z1*rBjyIib5ulUV}UUc{Fqw)% zv@Ib)*9UCpGxLsxZP^=&w4^d^Ns$)>8v6**AVDTvV7cBC62F-Hs)dI@?D!0nk~7IR z8V=Y3yg9=AGmL^Lr?H=pQ6+HVr85~@8Kof~7Z9mp(^!$%qGJ9XRpfbCC`_K0eGT4{ zLX5sX;jIh{H`DgE+Ar0(!kDa37Hx3tU;7!Gw2grfeHaWM<3F?+6vC#}du|#hUrayT z1I2Sq8qRH=4O0-)h_PiRj*$vzCjyPsdwraZG1SIK!Rr?efN_V`E<-51R;K3l`&Z zUJG9f)EPD!9q5w#d4Jr>S-(N3pRrljo-6SsnFT{z@Lg@RT0{@lCxTF|;)PJZ#bZhh z*HWeZeqYY#l_q=F{nhi@s|FJGKauE`eMw?Of~!g_OoSrH@9Z8*dH34be??MC>+@*L zvFJ99G4P`7^${@InRi6t*@BCV!&&CB4H)rIewAl?WjZ}A$Mg>=)(t^TGUz0HM*Y&) z-Y~ApSq`R)b9;~G(|R))&1iJ#&4WkQ502STa@!D9-+(w|1?ga8Q}2M2%8tBH;k-ty zH^4;V^wLxjMc13zJK?|$nOdf(N01Pp_9Do*`iE+eD%BWdCq>C*xN)2F#r&->#$PEN z9o_C^3jGq7@frOE;i?wC3`T1*%jVBE>wR*STmO;$0|lC@@GoVN>=zKUaE`IDmesP2 z%%tgEl4e&p<$f`1q4CVS`oYXiM(b^)vPu9)fN~PWxlA1tI)Y zS`AV7Btie)j<9m4*KJV@)+?A45j0m2)oVv)k7vVa|H}uJCiJ0KX?WK-S#%NG=g=jZ zxjutrxj;&_TC($mUoXaonHoRH3Shm97{kXdR3I4jkx9vdi=7G?$gO{ZD9O;>Nzp5G zCZi+{Fp5V}V{*~u=&-yKF^&*^&u=*&rT%p+ID;4KvFa^XuUPF0jp_dCPl#ZOy9d<} zFow@GOS)wYtIo=R!bwYkSRMlB6Lzy99w!4Ztw{1yfHVVv8N|)aJ^!_rW4_Lp8)NH2 zC=08X2G&nk51%T{(MWx?mc8MFo`>C?cg=)k#TTs;1fa1Za1cRAhR2~m&J-j`>#w0) zAJHkz0SQ;zW=0lRrR;5me)H`DV%KU5#MVHVTj^UFJ$G^04=svu1I*Hl;6=0zG^>?ihnuhle%#96lLGKJYT-=Q?sG=v1b zql@2Ctvxwh;i2uW82&e}G+mdSqKShHo#2ISFhqX+s zM@lpluMlh&0kup84F8}c&vBPD!R%NUa)azWUjE^ z=SPSarN9M~-6pAlDM17rA3O43ZG*S0^i$Uvu!^6}C=^#;TD{$RCNFyhxweBp;U^_l zI*0{eucht13nE{Bysc;;#}M-qdEiJuv&yPwWctEiiQEXAeY6oqR}M>3jP}^b7&`?+ znhS6_{*SWLzJ1dIE1NcJZgG7)C#$P4qh1KtLHETa^*DeBe&Yn-(uM3X?cA3k) zcPM%NUYdRwJYL!Cx8bgOChRqp^UUvS>kZLCXNG$3V$8n3uq&a5 zF7kwGpZT#$SE=3IFdX)dZ4^~p7xYe~8aCEmYx(HcOUTF!D~}kxp7gb#eLDXpo4aWG zDZSPLhXHVZ{-52&lZ&jkDQgXU*yeONOob`uAyY%|c#=3hBi^`1yLrX$$``(FWo>^g z)~BD9GI*>P@V)Cp^qQ#2%7O@WHCdrzTrHZX|BHjYg5T=JYL#x;Km3egL0WvQ%u%cP z^;q66UQ+b=KG)L~v?sGxJCxEUhVCQX21U{+zKEWqk>`n1?8{`ufj-U~RC>^_pz^06 zpS*VuquWbMY6|Jf?HF&?LA9}u%CMy#@$q9b{u|3nsNxSzu?V3u`I_MHU8Wtm-dRYf zuP5G*ulG-|-wnObFAMur>}9DxLjNxy!yP(EW=?VD58T+xOJB4JRqNK>B;Z=Q_>+cm z5=0h9#iIA!dtu!6Y?K@~FO7*1XJ#*bTndH(_k5A-qT4h|okyD=w`3 za93DvzmkvbBRKnIQ771kIACDz4E7ZW-}qby``^7v+TJbRDXP4gT}UE7GndpJKvSr6 z_CpJ8gu&-}=_K?2ik6hTBqQ?6bx?y;utd|U;ZsoWG8hVpaGlB+e`LdY@1iW~=O%P5 z*xuza_1@I^2vGP7Z{*X@BDoNIwMR*0hbbKx?8xe-gBc;jQ5qRi;27hpX=v!QWyF4l z($mtiT4O^|U1^_d*$jb7w2JA!Z#so}OV7P=Xzf3~)47Y5 zHTS9AEb1kJlK=S*K3z&ZbN*c=4JnyhG}cE^bayFSBO#B72ooiud_xSm7!LJ^<>d?A z@0GvB-$}ACc()MTaZLK73Fz`EtOYMu-SyICox#^mus0tySJBOE_Dkn+gz>=NM3Cu32ntgZw&R%`p97qBxcP!+!{-DsavxN=yxs=dKz~y9dLc z*=i5NwEXf$JS)GCB*Cz|aEM1r@{aeHxMP$wPXJ+wgV@QyXwoTtWL<_7^r+&UM>>83 z-{x8I8LV^c)kBwWf&;05aLg3m$2hP>#1tQ3* z{HD#nrd(7SM*i?;8+I(DgD$AJ8ut3wnKO(&=4JdDyL$%HDDbIB09i+v1URXuH`ma; zcwCd2Xx>8v`OL$WRebKUa}GJI2wEG%dsm!dQ%9#}_oO(`W0-g%Y%pIXl+C*2<|z>X z8^Hf#*5DZ>oZ1|e$DN7=xoNqs=~sW)pGqmeN;+QMfTlmwkUYLEE4P>GbfXSCn%IQ* zAh?Debu@HjXnc2{;~4v0Jra_MQ%mK(D&l1ui47D3T`4BWr|e+|8#EV^h9F|gEcf8a zW?G7i{7s1tq(sBScr8CAv*R`;F$rP9v_pG1&n(w&e6nSP`{MqtGT~EBEysEdE2n&M zKl({eH~VuI=m26lWDsH&hnBQA|2?GDmI6zFKlz?Q~CKB=QC^B3EfR)%`N8deZo>Z_cWJD`uWM!F2N8z+_ zHUYIVe^4+7R6Qi5BCMNMu?m9D1)0eHhM6(iXGcvg$)?50e9qd5OJj}-8VCOj*1 zy}%1+(}_FqQFt#plW(tbwwS&6FrMGkgS|WFs0qsvnu)Oye2(_DXAjXIJ@? zBOAEi+k*$h>KKynP*H6(Zn)<33<`8)VrEy9q-{?BKylX;g50?!mNt%_9o{p#tOPc- z*e9LP0K#p9N>IRuoEee@pz||eW&i|YJ^p^&`X-f#q$uj4oIg$W8qjx=;Hf+K%y;e_ zDAtHht3urkmTDDE_2P4<2pa9w8ch|6TYZdV;;U+XUM_%YhG*yJb%0Pb1f{2^XSXJZ zG(N+i7C|jX8mHx+i&$;C^<%uA9N11QNpy5HQ>UDy8DuVxeI~%%#C0Xe( zxS3P0c;!c(ID>S5n5eQjS*3G#8Xr-$M(S*&?%5C&^4X9)JvmWtUi*^ey_aD*3lIgh z!Q3zTm%)JN2rxfp7GQk&yAP6jlmgU2#=PO@hpRc>?g#la`aIw1X!2pfm+KjWm4zBZ z$A0=9;6JO}f6L5>{tOknJAYYhoS~_Ggpf$xwHp?4ii0qdPu|^mj!7coXj3-F zdN@5Z^F#RhJ34LTUw~9svwmI7+ggA^=JS{R=1?&}fO6b*QfS{6RTVstoZ$IUC>`KB zmiYYr^Ecf4=M-kXB(7vv^{4O>Y(rhP0prTdvb89u!EBeGh6C|8%nLQXBD{8jqJ~4S zQ8H3e5s!<5!h1sZhQ1e)|IOweaslX}8kp16c}F9!m~mc* zCU@lr!xb=cp$cOA$2XP0F6;E8J7JfuIrAILp7Az1}0A<$7!O2Na{N8pjy z|HeOAP9K4h@S1gi^;ULV(`^7A^!1Y48_y~M6P7&Oc`gs1&o=<3l=Ri$a8|4yB3m(H zuJ_UVo7PFciK-{fu1j1G80eUtq^~BSFDnqUh9#1%V!NA;@ydx$XWbiq?5#6ByM1>gU5UCJkAVE$!_#$&%HB*S&4TY6>2lI91f-fmR2WmwMsDdmj@7 zCr!e-xEYLn6qxoQhbQYr ze&{pY|M;8Pr%+5&#|V?t*8SBLQAduU2tUJb-srQxWmK*?h%0!9)akTRODg@(z(Km>BY zsWVPWr~7vhaEG+^{r(K?0dxcqFNR-vk^u2WFNr0GzXV`&Bt^MUNLfJ+c%bio?X4ff z%oU4a2S`W+v9t#uwWu7B-B> zt84y%N$*Hd&kb+K(s2ZgLS4$7wx0>JcJhak?>b^@1a90mHEVluFP;8(pyCQqm_e6x z%39o=zcBNMzpw3WizO1XR96Nt0K5;af(gjc2br&Ac=A$#>Z1Y(N~1;0S}^GP2KKWU zr%JBWhi4E*H_NDRkIYRo`|vl25W5>*N@`MY)AnParbH--#=@br{%!$8Omsa`^w9%o^83Iih_1a%+f{h(Ba2i_ z8W7PKz9|7|-%%~jc!Xf6?e0j5My}kS8){vsN@gS1LP|obWJ7JR7s?^k(#?SVPwHQz zggfp$^m~6DlaLG@%7<<{MGW9Z2s9RbId2E}sFn9>q6F^#t{ay15uBkKgMQ~BUGLsgicJ}iVV^~8k=gB9Y)ixQ@Qx*7er&IVSuVfhZ53dNO7V}EQn<13j33EH`+ zUU>*Bn@7c2Mm2(br?HBh8LpOo~(jkJF(m8Z@pFQgH{@!z4=Q@9~*>mr^W8I(iUDknU&sTT{MUFpf zDc69KcJH2=WHJC3Xn=dwl`f`Ao?S1|4Kyr9xoj5o3kCY2z&`X)n_O0gc|iA)13MN9J2m{1SH#ArbcBml^?I{#lM6W7C^L@wqEMO6 zv*;raAZG7>gV^ou?ZfgCDC>|l3&lgIfQ{C;Nx8jGy{@jo5=Cl{zOKO@JZ@>Ts?>WV z2Drk4Del#uL-vP)OlbQ7e|7Vi^jD{L9UtZ44XRkIR*pWpfRW&F=hRn*;{=HfI*T8D ziJz>nIsM!c&JR!=kKywM;uSV?2?wI4SggZrdKGSWPH1E%zy8;)>ax;jAJM?7eGV!& zD}-BO@Tnp-YP%mORdKVkGrM;8akGfvmU`FhP6IG6yFfLj^!NCJ@r4B5#yJnbjXCSa z3LGE$f_e`IGoniO3Pbm46>8CZ_zPDY8C`oge0=7EKG)4B!8cy!80XRM++X+~|0XfS zgS(%e`_VxzP}A?IVK+$9gk&upS|2%qZNc+fC8MD9`~DS)6->@9&HpNG0M+dmgIMK0 zc)4bppzSYQfX7Mhqd2y|yvF=AXM6h1cj2yGVb3v^xXP+`Xb<*+ z3Le~1i7HvlEdNgNm27_^XSWR+4Ly`@Q&U#;iuT>k(6RXWjh`8EJq#&3gcpU^oweR~ zXxYH7LV-i4OH*ZGBQTt*!5*7gn*UK2Zl1=$({RioJ{r=NaUIAa_T_C+CYE{c)atezDIBg_V($yR{B$VCQuxBi3&0Uw*^T12>$8t|{K9 zI~Y>|00HDcU}(kQa}_5QF6MV=72B}fd~GW2-|%3LB+b=NYgZLNAml8mEA&39g|{p9 zbz)0hq1UMGIXwiVry(0#XM5H~n&$>}ujhW*{@9ND^9fHwm4;_60#>rw-B13)HU_oX zN^@(NBLlx5S#%a+PtLjbB=2KR1RT8zv}u1*{$8?PcS}192lu{N!x~cO;#}9aPuggE zIh%PXd0FYio6GB%kFwH#lgl)+?N)X^)pFH*HXt--Q&s8<9NJ7s!#GKZ;Zi|ae+Zh8 zgB@feEEBm5Zat2l|u}B*FYPYM^rVFQA(NTrjQht|-nmgVX3f?m~e~=L@ z0?iG;Y2%gke4QXsoPkfZCtqy}@j&dKDq;fF<(TJB?_x%!&a8!vOUbpN!; zsf>|gvRzWT^ri|iNt=RL1uN1t=jv;<@cI(Dm?3YO@`@tS1etBVv6e_rnfuz^JnaPS zI-_zii!qDu;i0#WMaq#u&?oxxxVLl`vqKGH8=)0r7QbZwbkqYgwWT zI7H4Z({mdh40S6?ep^H$2&yx)NY_=j@9FpRU0;4$o?i5nQ%wJUI@Y4x+UVyNl=Ubg zRCo~+W>pMa76Zq6ECS`YZ>_yX(b7W#;@*JePxCMB6^ZJWKP{d!@`%=MFaHEK6^MLc4J{;d;^a4iQtGvn@g)GmxJK5%G!RaeqLnZ?#Yd1LUy&Yp#FZ_ z$p=pr=(cLH_;fMk_sr_mUp$JvHMwrr22y)T7&!N$SA5XviT`av|S#Rdbf9ln{*cKmqCTvd@S`@0X;E|{V`aL#l zbdtG&NB)R3vkm zy}DSQFyD%15GnpWw;guy#<|QpL})cge^xikgR*4H{w2=dS96(^89n{NGxp@4XRPLD z5&Zg(8;yrqUA2XoAvN68?ydq8#b^6_=5Io^DhnDOGd?^1IbYRiLo;18Q+)e(EMRy^ z$m%s*M3#RZME^VX7DR^9@oNKk*qYJm2}{fgizVq^=;Ph!txx7TJLZE{Zox34JzP1F zawg7W^+LN}Plgt`__|54V$ppuL4T*J45^}NbDi?>p>e_I6~1D{eAI1VM%tbSD} ztg@o}ySjg=)_@+&u4^}*@9k=w?@~B9UwqBakmSPI+;eKP?`%P55rKv~%q`G++3r(1 z+xsT=rZ6gL);SRjQa#k26WbMY-_@*~`%{e1lz*FAYzs5GQ)EV8M=RUKnLpn-GI)AB zO#2~UQ1 z;gsdO`D|%qZ$Ksjn%i^0$L?n3(e z0f#r$pY`?n*0WW^qHRb9alkP&NXTg;TDQ_B*}~ksWaN5yd_2o#XKD!_FJoV=18`dW zuyL-6%CDV)OOGO?8$i zbPJLWWY2xVLJhKCwfo1ut`ud`D$m8hKe?;`P8DFFQ zUk?#2smdxYR&jQ6s^Z|M9dly% ziJij|Q=`@YjcD`hleg-6d)<#5DeZOPS8-aT1#6?>)>;fLuC9Y`YN-^QkLw<@C&HD^ zeHbVYXVxb@E}cDelvOXgmnZvEwqi{?_RaHQ)2=nUfp>}5dPR31TM=3(q3y#z<;I(u z{H`fl&A6#v;j?Prs@hoNo%08{ZG={R4)3ueG}O+7;i`24-hr38xw*o3w{-n?x7S5( zmO+?eLXcei(yeI=~7 zv#>*E;L7K%l$W^J_r;)n9g*wr!oAMdmc4~HrQpVkWc^bcpWi98K)X|(1cFlGEv-u8 zRR^*hqhAgCz62qRj*xfw;?kTZe*iBI{hF~N8rD`5$4r47txB}x!Vi=$36|ntoD+*T zI%Ugu{^^ZD{}8iKeD=3mP6Mi10w7fOP>MwZF7Hz55^_7%zFUPv(fgjqcD&uUdNY<| zDJeQBG$2naYfnQD@%yXn*PSiaxS!*E^zXm*CcHs(${GoiQ)v(ZZcsB(2#uo^x9F?L z6a{g&gS1CXI@JzYi6rz%JCf80^b7DivHyd9DQB!}am^OACICVmc74q~B2VGHnS!{K zmH76@#6;--qlumxxvzQ2c?spzvbS}gD7d!c%FS9l)ni9>RvFxpv>fkjS9O%SYKLX+ zI*iz>krbLewy7zlnyz=1tMu3y;y%qLwj2|-oywoL-z6#V9g!)X=jD$#)?jL(ZS!gM zq}^$S5R|;jRfQu!Fc=A!DLUQJFUrHO2si7=ugxgCQzgcGtt$9zvTQV`9S6c8zFe7M zZr>x?g|+ordhw*c3+l5?XqU6BjO6LXH(~{0I%aMVoG{*Xz#ekBJ|F#nVjc6YOs3;M z28`i~q0hqcmiuGFn9<)snD-x;Gngbe3eXf#ryzNUQrilTsRvIV<7)~|tkJe3Lw#=` za$y)BVmuFpDb>Wv>SXjwD_Ya_sdf^7o(Y;GEt}k_ueIvG)!Ksdx4yr9OLxsgMGJMy zRqzK_*`=S;cG~_R2y&kaR0mlNB=%r14jqq5O0=y83MgKZ5seY7(~dkQk<~97|7NA} z0v>kieGvTU1^Zl0)>UFxL-Zme)9+{-c1iIqkJFPAwjJV@a ztmijJT$Z-##2lHGs33ffZFDgDdCg8r+L(82__)i^p^QoB2Vmn zwN10f&Db;j+ry1Sv|HVt`NcrjRn|b+>TA*_C(kw7R?;Kg3Ju$DQXu`fBRb9bY?&tX z$O_=e2d()+BFb}LZH~d5) zX+lAaHa-q;&_5a^g^%s_#b<-Hu)%Tt7htV#eZCInKRwzWtcFuw2 zPYSd@(#LC=7>i56cWfK}9JMU>o0)@o{zO{6|7zRg`mpW2<@gMAdNCU~*5P}wy{}{Kq|{Z`5Mx!QvTblPJAjRG&~9RW zt(HM+dA5SA*+Ea;+Wf&SsRr#`d1u`(*0=Lo*q+cI{I)5TJ=zUOTX7VPjRiEi zsE2PDRsgOE-w2pclYUFE09)(_x_-UAy+stkYvi3$L6~!6{CfJk+WrTj&RW6~@U0(= zksEk52ewvFbx#srH=n8*VT-Xl#z2sD4OxJg*iar{r6yx0f3E^ue0l*suhP-$5n{hj zZl<+gGhh&%fuuHm{FYGfaid`~Xy1Q_-;3k~7MDr12=GeO>@rkJIBFSe^vB4F`Cffg zt$*<`HIixhzJ^o1t`E@ISZ@jdf7G7k+g2v6o( z?s|XggxX-YH12yaVcu5sr>aY=|GdQi5m@DsNcy#~4rkjPYgJp3I~!017ci0h#Q(i>)VPXx#J_+%sNd+C({31X0J>h11WdS(>BVBQj0d!~i>QryRM4k?ZYS?i zE3=v1JZE`WZ!rjY8MR55~^J9WLYvFZ4ERTHc-OS&@u`CPvAO*Z}xS zUKZFzApN_5uv1g}0yyx4EA6tSBWi4h6}7?^>32SK3V9za_iri1S*t|}W!$9HLRF)v zwzX^8mseDbou;}d1R2yc9}=$M{d!(_kaqLoc7JAi zZQ@u>PJKztrKjF?JD`~_)nb2cduE&gBhDQdmTFw*@;w3(kOTVlXOcs$kgu=(0*|(q zgd5vspXSD~E9iYQ>X}?zLpg6kp_+&R-#n3FXNBL?cwug1IOV5z=Q`t9RPOb{7Wdh5 zp4<$PaP~x5A{$f0R*>8K3Ua4)!zt~igis#LvTrN*b=nnDWm4A^6$MB1U3oP9U* z2s){C|JIghE#Gb|rN0PV-d?aRO(zCp-+4Z9#G!}jJ`9@znH39*W(Fsjk z&ss7(gr}>xSE|~mC$v0!rDO<4^U)P*Y(5EfJ*Ltp@I$A^@MtLH;lr{{p+Dr)`cESz z<40~}CiSIge+aYb?aG5b)RvGk`!i&Pj0s61M9$SJX#M3uNqbU-w^T32Bf>4IkQ4H3 zw(=Z<%!65!ZG{}BWdxSL;M49j^e02aM$z~M8YjmkS?Q8KLZ8yrHK31LMm*{UgQksQ&JX&QC*Mb3%u7D+OZKLiXJ@r*{B{-pF zDF@RgU3D(#^S4aGR0}9UOJ?tgoN4xRgGw5L^|Tj>r#IOTtC`~sR)B8iM1!ziMuZ>TZJB1p!=OMmxb@ zXw6jkPt%FRzF16=+*5N_erkxb|AEfoI?XMY7Z?LN89|qX>`^ zfA3BKMj^}j$$PoCfi=HN9@@GTN)UAV_7a8d^++@-ruIbDQEc@p<5?n9>axP9I`)xsLSF+QTZh zd2D$~QXW>untwIYt+=RMI=3AZaci@slAIN4>sJY*QHx9)1LTRK_xvfDDxC_L*mbh@ zW`>|P`fv6>kw>0W+6XC`V8JpJ<>}$0T4IB3;=OcX&G5- z?P-F&Xww%*I(%oSE0x#7FTNTnsEM1i{WcwTPqMb?&x$eIx3VpD`1(`NYAEEkVgbgsQKRD$~rHLZhW-D!<}@7#f0l(jRe zODv#;^bP9DIcIn!#F6T<;L!d0G-PhhAFvwk>xrv~`W%)vx&5Fyci)jHCcT@dsLa3S z&xEFa-nWe~z1_m#FJx}SaN;gw?(%MRBYbD}=p7OZw#1XNrcOZEfKKk%Qa+wEWIXWg z&zr(>&NT*_kkDcF?^vAQF>!!3JuRkV)305sM$D)O@PaU!j&N7-QhVi#hc&?iA#^w` zPf-kyvmv-sYI@!-#q4cUrI?uax0eE}(*K}2jM&NoVZerEoF}Pxug3v&Sjg{(WT`#( z7wKYh->=d2F&^I!uwVeKVL%)&9^>n%CvwOLU@}fWc+9_MeKV3*@M}KV-1OD^^Z)pCfWi10N(J#?eU2-59hi9W&O#~#eu~6-LUxHQuNcUhQ3RK z+Wc2@jZh6y&LNMUpIVu&x#)xNt535Bw^{Xa(UHy}{XmfVX9Fj{_s zM(lkEstW8M^4?)U>nksCe?isH_8ReHiYbfldE*0;CV)Hn+S-~XLe?}oy$gs5zzP6i zg21m)3QYUrjd;H!P%&(YpUYlgy61w#hIk}qODpZAOF$y>Vtxl7Ji^t;9N3!@T3V#o zM}V(=<^NCT&=yqaF9X)m>9$q?UM|HiLr*~2{o?W65C zdkGNK&ye|=NuKU7cIaU%(>A}}G9S&ZHa;zj1#mI5G^`A)%3U&`J3WYa4;hoCpsk- zU!)-=r*dk)9QTwVcVRcH0^jNB%y~*dO-@Zs!L^HlzPYma;QliWtW+*q2y z3i>=?`?#7FGk{iIXy~~a$z%y@m%3ju;N1zxUz&)d0dLc)a}TkbkxiEKbSwICoxeuc z!2%Kqe`KZ|dmQ%SfuYj}V>PQgPDH}6c)!^9yb>(V$Q@e@BMU6Rk69n3nd^9o3uqui z$}M8TiV<(E`c+7-RPDt`S%7fGtE-aHfQ)8g^RV}eEvciDF|?aB#UNfA^PkA#6%u*` zIE2Xpskf|wc%rrqiC&W!vRd@NU;+}yRQOR-BP30*^5!d&u^HdscSLr<_}_&@^PV8b zMmq%x+Y#^0WZq-jcN0X!}W(i&Bjs>NkOBaPly(I3Kt^x zs5NH96G+SY6>wCd53Sr}?`9D>9swnEIgbRt1kKLR+nCVe_t&E|$QO~#E#pjl#S>L{ zJlK>RUym=r5N4_55FCb5fD!B-Rm!R-?EMtXiV`ar-v-A4xLNtdW%Y==_j50($LeC|y zVpG&&CNJD#^0%e>WTasEX4hkJZ<)KQ*+W79#3+u26WRUW?(UiG9T61cO8j$~5I|SI zpt9k#XiI+ zLHKY-akXW=4={I{!mk*;`_@~l&6 zqiqgT3xUXGX$zME_Wo}*^g8fsBSS-pIe?Ves`S0C#0fA$kvalYU-AKgK8nd|N-SbS zo)0hm&?Li9%Dz~Bcvyl;)>?F_q@PDkuEjE;l($CsvzG__qOO@aF0*ONdu|(|^`sPG z`S$<>BF3p)XVYzfC=I7gCA4Wgci<-R!Li8H>>mga~LqB=QmuhE8%YdRLQHMI{KOagn(j=I4-Vkl@aaG4S{L`5)q$` z1?;r$J8W-cm+IuG-P;5{Jfj1#VoFu}%R0>Aw(9sD;=S~Fp_MPkzt$$t5K+YuFXn!z zC^)bmC+pR}5u_$&F97J~FK}Qj0a9=C>3r7HCX~h~u`kfgm%w1-^?~FLE(Np)hLVIN z0Rjs+E+mGyC2)R+_gSX5m-J;up3j#`_m=8ZW0_XZIT*p!K0VB-0vnn;K+~CK%K3{a z3DJ1BizEwpUzPb5BVti(0c!G<3p7_D{B@H0HBQ~t1n$>oyIT#{d&hHYW~_@sji-8z zSrEWUJg=a*K^F)j@_2ot$)fS@YWwz1J4tSx!DqL8l6?L6ZjTnY&0C?Nc-jOtM?j&C zwl+4(qL-`Rf=i_s*7I^w-pEB#a;0>40Akg*YCCRXJdvDeTbntlXZLa|D=Qs`z|{8C zKAS=CkJ_m{gO=fK(JYLCNx~l(cBDBR`xI|`J!wC+$^d`nB-_AQAa}PHZbzFEWD(({ zB9J>bNdkO)^3IpYhe6DsFj^HFDk?UjOIptU&Dmyg^~($tgWq1&WH|`3=jD#)s8lk+4Rmz=-{=4+hw&7;aVUD0r@(1fU3I$OSR9m z9m!%Q3aqIoLfAoZr|&+Q0OxSy{8=u6OkK40){^rZq5L}0uQ+gJ*v zIU5Y!_(DM?(YBe?8cJ{-`y|bna6}AdGbePft@Q;@q192+3z?j)i^ebQjUaMj&O0-+ z+}ye)_RcQnzL^FwfQm)|9d%MT6+===3j6C&nR6iG&YVgpCz?*r zM&(429%fDo{v=zLWZ0z$SbMV=P*}t1M2$e=$Re2H#WQ?)3DvLtyqzjwpJDj8mA8Lx z4T(P7DU4SHU4$yFF0VPj^f5KNH(@F*=I;lllK7?wpztyRtW$0Nlt~P$<-I7On08vs zqa&{lETASq+hLF!$rep)yS1-|b(>1I*xthifO$DO3XnQ0WX5ZeHt*sPj}vg&(OweJ zp7cm)Ad1-)9Bx$XOfyKxiBs@9-(Ro=C;F8uccz0+cLW#$$X)&eN z2-hi^2S7ZlCqT;pleHq|>-C zKjp+o`&myuHLM>gnKhP-zzwsD&heL~Msa)UmOzUcBb}5dkH`qtYna6^CNb&)zCjQA zAWoqMT&YR^SOPf~KAT=&?WDLU79HIto&)SX)h#Bx6@V3FUk$E}ZuJZI*q^WWN##tA z@-ez4(+*vQhX_+h&5RP;B7bwhBNVC>=B z$7VW_82Wq@D4gXk&@_M99%oeMcrOwIbd;!1$4B-u`zRHskDmrZ4#>*dMgxZChDbRe zJR`_g9=26W=nF0Fufc>ALUN%voK3u^!|ej3Z@p<{3GIADawtK4qXIRFlz8}}N;F=N zFnFTBe)b_nP4}|9EmI>whVWJkGfCku9i`isdo>YP-wn$=I$FoX0opzzD!}LFuruQ1 zO%wTW6;uf(;9Etq8l*$m~M4#<&ziz+1ntPDw_CH?7F%od^9b6XAL7?Fb)~J z9TDmrC}bSRK9ff=wVCOGy*MR?T08Rx{VpqzqT!syECIXQ>RVD1TrwQpPrpXc;rq8D<;?VGy2e!f?CJka8NB4((aRYucd`D-(izGS9Wp#Bf)|8kKEk669O&jEA}yEh zd!qb1QBv?J1Ix?Z-En|L0MVtWk>~m1OdEB1m3#*~wQwU?w4*TN)?44KcniDa-TlxR zFEcOD6%>JLdR!AqF+VulO*$Rby1ec?R`__lzG4p%d(|XGaq$;pd0OCvKu0J3$@P+l_A=3=AEsI>vU`?s6;kWf z#ON1OOkt?M<(D-DFc5=8R_uV6{&go$E+EsA(?G(bvhos(^C&}{3B8!E_#{c2Nsptq zEGGpkAQ}%*qp$qV3-Wib9qH2B9W~!8Q?7p`HTY%oZR`AHx%oRwLl1Sz^(8Mok0b$K zG3xcTZx5?t=suU3$&k5ySqVf}D_}Q5(j1#~H~biUN-Q03ZGZ`az2+iMSO)tcgUeQB zEBsAc3^{$+n?i8{j-ug{(Qf2Sulg>BALj+$bBOhJ4L(9m=SFm3RuH&;C5=O( zpppg?SIbS48Ghe^rxj^+P&lRUL-rl=I&7QIDv9T;Ht6Qkt*bXj{eY;G_BrcY%yVv3 zqEX)ie()eHY^8?Xh^++=K#HY8EnCKM=&&fjGJvvVp@q`2U4dshm~ z&d8H06z?A_=2KyeSwyi-F6@Mc5wnOK2H6#nbz^DMJ(;?*(5`t=tI>Y}Z}F-6dhNPf zk+M+XTsv*I5lue-i=R`Jb8KXCV3$CN0DJRt!~MZt!!f(~>Kft@WYXH;eSOm*E{4=P zS{eY%Bc+%JTgfSCJ37q;uGG(i^lYkh@KPFbV+lM#xHuC8{3I=pbiJGwzz`9~Lk(l% z{6XTm%aztag4dy8TJDo2Su+DPNpnBZy-Em_b6`E5@K^hfnV{9_2RC6fbxx^jz>JS6 z#h12?2gzNyom)87B3|9pX*wO1)j_T5A)Xz75qcAyR%MI4p0`lNJ7_ZOgM>3#>G1pR zP2TYy8kfy!p-BoH`$)k($NR)9FsJy4g5v7<_~-#}6e~s6hs3`^LS~P{bo@KLi#kq` zrlk?SD|c`*Hq@^7`L!>^-Zp7{CR1hF32j<=_EIQvnxW7z%x-ELE34&CRk)q*d=ZfoMb2n+JbaX5SVVdJ(vp?ts-;rS*?K`E?D*u( z@@?%|F7L{>iIdF3twS~oLzm*M--6qnT1o@xVjRnVl16pqlm;!#pvT)wswRcDZI0&P z0H$jJ7KM1A5ZqWWCXw1GxD-8&0aryg>92v^7Yzq*D1D6&H0IjWAv>_t_^5T(LD7j5 zn81Kuxyvu!ImSrKF{>%vFloP%VvFs)rhi2MzVqnImD4N5ZyhCXXd8X<{w}Z3dG9__ zT2U)OCtd6MOTO_Xto@$yM6UvS!*hsurrTGT>4(bvV4Rt+HaK(%Rlaxwc=B@E`@HDW z*)9*)h&wUXC_t1avzYFeh_m{hp%O@6Q30z2g7ohLz;x)?XL`D~Yyn zZ)15Ma<=zY-wq2SGe#;gZoyyl+H3nq&I66Np4fxksi|>MfN@|EKh}O+zZbeF&8@u= z(l%KE@&1!%AW2Vf(Y)g^T-M4~N6o26(05&djK|F59QFKKkMeaneAZufhl`8K0v zymKyhSJI*Ct#Ip!licIPrJzGuZN|Nio{JCk zA}lmrB!y&Qk51*i_5oG*mH4v7KNFFN^9+^6QS6MjB!~jqm_>Ll(`y=jR;gF#I|vzH z#Y$(JBPpKe+)S<=On@5DJX}s$jO^R3q%Q6(IA2Q{Cp8Mv1$JjZ(W0rmPZ8A7${&vO?E>Aqoz1$+XPP-2NlV%duAC1@c)`Q%=KM!}D6hSTvD12f zy^%YoQT^$N@wtqdwynVoJ+pd9xR##@et9v}AjoTf5~dq5Jb>J}t?u5r2=-_ImhC^8 z#k*j}{kR{DL;CrI^A+E6Fd?t@&xh-q zB4eYQ;OS5?oz#f8`Q{cmOcRcGTuyZhritPy2K*aWl61rS`lOTVgPlNbR450Mh1buG zk@_rGHq{m>g;LK$5&Im2{N0Pg6$?ANApzI)<|D_8F43n045{mbspspbbPa$^_Bash zGU@Ge;4BFuK05-r|GO@^^`|@Lcc9QsdG#h+Y@O9=>@FP#dlPbzqKnwCUW5H|BPj*6 z!?aWs*Vzn?wJ9hfsUzydMWYVuQpnyW0qt@T&K1e)Z+sc9s#2_uL$+MdcvERT17CM` zbDJ`FfDwSg5)o-%egtODLE64g{W))I4TOcWFfnN(E$i;?4#-{N_q)CELQ+A}N1+e3 zT-N>`H{TS1dF(jah2q0M)+H?RPuKvsX~|XyZ{?c zt?7L_zi3fk`$%6XKWS&TfNVfG%Z<8xNw+67Z$wfXELO@mEDHFo-xheOC8iKyHHgzwO;XJJ!kE4L z;k#&D*@TAT;^JFZn4Yfty~#9(UJuvTh%tx^PjZG8`m$8;_r!=Gbpo4UA8=N>o{5@f zwU1SNI1m0Bkzv(bo}$P#X%>-?u3)(%BZ*nkijUU$J{GbOlp^Y3423 zs$m2JRH_tFRKKWc9@-|Q92OfXtlRhacK+U`PmKdQhrHjCOI951tk;bIzK)mxP3I`! z|NI(d@53swCZ`Hc04mb@VcC=K3Vuc<%hc@DcvpBEa|B%!qQaVYOMpQvS#dD4_KQM) zpp8F9b&m7KdE%PfFq%<1Fe?EjKVc!E&xuuU98caMhkc`7FFYzTh*Fwy^j%*Gbi^1( zTEb$8a@|jP5jnYX<6*yi3(Fv&EX`&ZNDZ8_J5IiPF$nQcrSjh4a=sXri#Ym-Qh!04 z3pg*I+=no>My(j4vP1^QWS-}3bxyT~oPEPwp5MS}!%5W>f(6hXkp1}l<(B|DyV2(^ z83Wout%n}QbUh&SDvT@PYG-)~GK9@;o#y?=x^znGz)Gw4U-?WP00%;HbIR{5k>hif z9%8M9S^-xMr_C-|aO8W>@3loc&IjU6mm8Xk=D-buzox@&*%E7$wY7Ce1UOI7MMF8d z47U5(^x3>~Y6*?eqqctH@~Wl9yTE*#ZAH5Ia?ez9Q8#VtBUEe>&O7RZLLj+6g~4N*M@L5=1KFE4T7Smb#8+*`VqGn~eqdNCk2QDI5zcRM!a};k%$XcA z5S&|EHDgU%Z1giQP!>MjW0F2pOXS2atn-~cf=j}?+)nPejM>WF4lQs!*TCS)FE|Um z?CW+8o+&aQiNR;H`nvzHz(K+J~Q;^0VDoHi)A zkZLvfD(edqCMeRsmUx0O$RA3zma-b1rQ+W7i(bX(Hi@!+*nhj(8Jo_p$+&?93;SN6 zvPh8xyPieTmNi<&oI<3%*$YA$g{&dMhOI~4BJq3a9vY=B2MI*~gw^s6UAOymG~^c& zKOY}Cze5)MVq6ee$Oucvqxbjr_}JWeUP-ImSlgj7b?11PX>!w15SrdL76&S<=a=jf zcE{bgo(svcPi|K6$hlnRq#H?@?UL@u>G68IM(?@)`IS3IbkyMdz2EeG7egL4=W-bg z9b&;JnenBl%uenNGCE(nTq6S&%F@~<09FU9_1KdVHdn7wkNcawBP7V44{}H1B#(n! z5k61BKQSGfE3M$x!91W`y;{4+AlGg`pGiZ1H7hm+6G3ez^-1^4_$=cUoQ3(XX`lrK zFAz=h(Ng3=O{VVPT$)9F;{!SQpmsVJF}W1OyiEyq~GBv$K5Z7{1XQf%=XC-@@S z%2XuSZ5E^|E=JBhdzATQ*J!Ocu$|9N#9am))+0?Q3Hp2_^Rs)+OH$kFKZ`LCjdMj7 zI+jw$$0P-ra_aa+v|pXMrJ}D_CeB)TR5*h?_?)Uuf*CWNbpN+!Q&e%v>}IFAuX+_a z3DkvZl2G$J#JKa;O;Vlha=i89%>H3&+_G8N0WcAe8X{FbJ43@WOV4|Dm5js4 zhk(>h)PXTh=oHo659cw5D<+84ga_4W`xCrKR>#W2_XV$})qA24cA-}k6hdwg5A>V+ zz`|R=-8D`G)&E#E*6!TcE@>~Iquv@%NK(7-zd`_E;lA}|Vgy?P=6Czo<4>-%jyok$Lq(RXIv1Z+CB6l{0}I#pPT5Ki z-ghavK0GQ4ohlZ$y?3t6d!^#`3}Fk$VaViY zUWS@^puXlN*6$eA)9#l;ce%*oZhS&CgP*ljDd^Xo^f)RCZwcUR`YVR%u?9c?z z#e11aX~Ew$%BV(MprX}3b8#G--iO()nCWjR?U>=`C(b_Fu2&u!`#LQ-)b4LYk7_0h zUPOsy4R{zX1QxsNj;Hicg|3{7U*!q`QN(=eEF3z1Nx0^VRmF)eQHt0JaJJbu7{a4m zBUWx;PCbn&;(C-)vvy}8QeGI@bfSkrLI*>EK*yt#_d9LYw%Z-{DRpqWx6=fI!>H3( zWd)M5Wo5B7p{6DhCY+z z9C`pu`@cDtc2hmmA7|2Yd39i{o*;KAagVjll*UUtV$Q*p4qpSPHBMNgu=i2CHL65ov_i$=Y?@Wt3Dt|5EnZE4-v;Pl!saJ&n?q&L zdnuu>9rQ-ke+Ym+8`B{P#O0u+t9;9NrT``3B!A|_t^;KsOhL1o8Oe3H84N_P*i;oW zO0RJuj?O(=_@X>3vgjLx?pN?g`JPmJMMQ~^guyFV7Yr~(3mi@-^aT%{Fq=2N{Oq`c!QhLVNoF3gJ71WnwdAOM zrFK0Q(}%o_#UuHT5(y~Is=`$W`H&~ru}PO48a&&f!UJuo(S@CO;t06nQB88H`%wjV&;%(uxoJMgt-PA5lK}{^~d_?h!ip(22@h zbTii&*g(M?)?Q2Vw}vq`X@Tu!G8muDgobDonLe6Gt}P*)a5uWGP83P^7`hg&Y$P1a zA&nf&IX-s3s!$-vAAg$?uiT%XpJ7KDP8uqW5p`j+pS%6GZXV}ZYM6@Wxr}ks+RZ@o z#`~)lOk%?-?Jps5Ojz%tXLxatvn6>e#ALvMtYrcq+~KU_p?J*1nv4Z$Tg*jrbsIhw zZBo>FEBzEvHam_UrnccTUOI}J_u=cfL|WdGU(>`6-yHc}f3c)U?#k;b8#G8M3txPNxOzFg)jChF$vbaj#yI@YA*dU?o; zp>n-v5=6V@wfbP4!N71Cs?dN|Kr`uaT10n=-{7+X86}TUh)0s9Wu}q{3w+6dgFMkF z9e!tkNv2rn|AeLMf>D%{4z^L400+Ah^DuRLzoJz7ywWX4rl%xIb^G-bbdwl{?-LO%|siTNn`A+u@ z(!CAW%BNOASe&%{&tW>AKo2{6YhXzz|MRlM9*xXUf)vY*Ogp#rgS#Z|MUoK}CJE_7 zpnsu=Y4oCrRpB=!;OgE;VL(37YI*6p`{j=0oAO1oLrsmIAzQEhVIRt`7if-ksb{W2bF>rpxSc*tE}JYw!tk_P(!tQT;8%*!N+5igWE{3dZMKKD*Qi z*ExeQ0JVNK?%0~#VXszZ^V&4+)pw+A8~K@cSU_Hz6sRUc9)0uzkvD%n{`xs!!XLJL zMfh6VtV-3U&^sJKiVS##NALVUj8_&%8`VAJjr6?P}oKSqO$rM$UA#jv{Cd zN5*?xRjXD(sJH6hLw@ZpEk{W50>Ds0lxw2p{l4io8=CB;1|Q!-je5@v71R-SgqtTNK9cmv=xl6kvQNS0GqB&T-4ptC zyrE3$rzfr-4^Lg^iznvXyA$@vQ+*bV#=TqhC!BJs&OckPG{^n{Z7%#t9K4+vOOT8K z%d0%45IVZXia&9K;kbdb-qWQvQ38*NZ3T5xh`f6Zb%nlb==50=gGK8c0cv(99>sa; zLFyOrN(8Xu6efHs&s2gGn4SHU;f1}qfiP6pWk)RnLnS}tn^wWqF@K&ArVh2i7^vmR zX8+{;Qn-C6x;V#-b5ML(L=+6@vSH%j4;r2ZpR_KTZ^00VYIX z1@oJbJg&ljN(boEU7)}F>YM>EN5C456cK)0zrg$kki9Er>~7ftUT%4VGL0UwQlo_p zY(#VO^0>I!5uXl!Y(_Cz`{tEC@aZ)o*D^$^Q~t3rrBkwGKQKA{Qbji9D1iK+8l^Y; z74Tep3H|_EE*1AjPrCNeY5L08@-SAmx^BtfNYTjDCC_)#JnY!j}y(Dl6g@rrR(iwgk2w)-pWd zEhU2m!w>P5jvjuk^Xo%|f!hTL?JBBWmjp-v^Z$jy>8LP15B~HB;6Q*X?g>M1E0`@H zT7Wb`WJ01HoAVUe*oGwJV{49qp5AtGe((dUgHyy?ARc7z#v#nD`(PJ&8arIr25(y| zIvA06sXU3dv^_dL+H_brfLI+#BuTh-IpX<*gk_o_EV7z9CJ`tUCdX`do!y^Gq?8CG zaj>!bjphb~4Oo7M$mVnO6DsM%D#&}K->75_t|Cu-?GSm)MeSj0dk^4gK8Pa=0fI%t zj6p5S2XDVLecnWNr~me?@&Do5poKY{@1HgyV&vhLBDxg`+YqPO)}Sa0hQf4`wG@L5tvBO^|f@%qECp_j4kwhIHj>7u)R7f~^+8fo_C zXIMG(O=QoWqCkO!gR=0;*o%gT^DtqoFgoBBFuUO=qRHkb-&vc+$H!&ih+-N&L)l&N zAdCBoN+$iwJsa_8$suiQ^A#f&%u5|nx+<{bISVj6o&Y#bC;SKE8YHOq-h744{eBU#p1QAG)HG*>n5`&0*ds6KnNACELmpFuh^+~Phx8oVtz6in zsde3ljQ{>^4wjQo1@svo-eCZe_Af>P1pslyrKH-u8U;XCjI7hn4mYx05D^8)0yuX^ zc7tv!`MJ5@WyU1}{8)kG`as@{O|8up2oFlKo}qnOo~$gU zl1Kn~f?Iz#mKnZ3?!=Q;R2Yf)8TQ>Q zD>cjCGM?XJUTVzV#Ht|-(pSJWwhlvc z%ZkI3iomGg50a@<(2Sq_V5hW!AE*%2YtaHSVTl{tneskto44J$S7c4T0}*R6u!l)@ zi|Z5B2w(q{tJvR9BIA%v9DHC0dLPN!8iJ16H5t#sMri@1aw)K4+z^dU;-s;v_+d(! z=CnsJN^)5ca>+_KptopnwOryrZu>SRaczVbd{zQzX|F3(Er|hTmrR(+s{zP%Wp+k} zFvt4zUIf>!OvR}Wr{%zvV8GEOit711Y zfso#tR^t4!YezHySE!Aq<0h-}f9JS9Em3MbVkpize_K_O*v9)z|pMX*XC!&@yya#c#`#*k|mO9;{_(?Pv zdUhbG0^Q2jOk|dN(;mUpGvZ<1;3_rJ21xjwALKl1kJ)5*MYiqAixxtzY`P6<%m!M8 zoMR$SXOLc-Z>kgdi0UGQV{dVOHAR$`l*F?)s;G>ajhft>x@(!v(B%lW8@QLL8Tahn zz~S4g5$fN7ujD({VbY+eFAQN{t?%8Y9p;pO!7{0Pc-sPDOEPkwotpTSRh~b$4B5JB z2DXJjEdxjAqD+~G^9UcZyr{>&-YF?kc;g;TwRnY2#We5RQEf#s9Bj*U;Pla((-Oh^lhz=HvXgQ$RB$`LY3borx{h<3>wK>okyjrRtP{r; zXIXQEZOCyii&D}(lq3eg+c;cj7u{d6S>)3V2|1v=!7Wt{w9+F82ddu zluggrCWlE_lx-yRG-0H91*m3FnY!ph39iiIA~{f1 zVU;w1QZe6vSZWQ6=LV2cErtbTk6`e8Ie5@c+{I&n4uMBBQwUnS!Zlz2Z0NZolr4Jx z<)1;DCS7tv=Tw9FjHd(l?~s~iT@6d7U&qE1iDk=n`jIcIC$<{j=1L)mO%SCwp}exe zuZcd$aRTHciUdWA8<1R zkVABFn-F|ay<0?`O*}F+S0{UFsUsRPIn(Utxpg_4o{zA8w{b_DSw*Tlj-n=2om!n9 zB4C&4?d#hQMSY+~tzl($mPqf{qve{eoTGSx!FF~#_|UnB0xE>1kiz`~){D+K)sC3g zWm)*nrZD>KnumR5B!Lu*twB)D^aL!Yo`w>)^lhjLg&M9A=XGUlUmjagu-Ndru<+lA z6P+0ZhRp+r(mBUKP^4f$Kh^8FdLH(E!rKd^ffs=pe7p}LMaqz~t#Y5&`R*TfYttVE zTb&I8w#l~c>YD?RDbgCgc>n%=d+?6*`uTDN4|lJ?V0gkk64~#8O);hY62Qyk5?s|p zC=PG-nup3YBLy`0%74q5(Y`nd@sW{a>MS3o@l3hazU=#?kWA6q&_d@Ds7X!DxyTF8 z+4EB72Dt~x*_zQ>dICx%pR7O55>iS#Pc5c?6zwD?W)%%qQWl~eO`;{Mz_f|@u*6k zv~ySF?5j@lma>gz()K8xY`BD7^fYZJcS1aEvHM3Ewn6I-iw83hzW?{2=plj*t`NTn zU}6a?CECw~F7jv{ISV^CT?B^y9QDI?wqf{-xuLb6N4nym5S7J8t4l(PUW(wr=}A*9 z5Fk>0(=>y}{>E#Fot<5zQ<6X!DKoFznJ-}$=r&Dw?GQnZEN%#RIt;No3y8}Ve2m-% zR{Q^BFpz(^z0X>gCXLU!$l`GGYFt$*9JicFyghjR^kXej~y%}#<7I`h|UD$Q)d$k!eIc(Y5tn|WEgLf$To zx?B@u4(TatmWMuSJOWCK(>xy@J{4({E%MHMPEmESn#*&n5Nbd);&Ep(vIq~ty~8w? z8wwe|?q|izJU7pm=N05OsfxHDb2lX8RJAdr>re#Y0!Q~{3Y+W zKXVzNm{2uA+quZ~LwSX*KO??={uVpkT-bPbA7g3h81(ET^aY!(wUOuW{+V1jz`MO{ zS;Y1m(xb($o^pX8&L>E+@FKc4J>61zl5(5m3Gcq^Xx!|RS)pFemrAxRwr!)9!|a?# zSa^EUR?>zF@+&<}dRC!K{hls7Te1_+ecUYpw|XrI?2()cl7ZG0K8Jkhz6v(I#BARa zPch~yW|1hYnYPkeFC6egkh}tDx#xLL1x}Ba>ZPS-OXz5`H#S9Y8-xs05YVjws}E8P zj=a6;D{4Zk=znK8jzUaJ_-AOVMr%}!%kWFd3L||`RB}aPZ&x1F*42!g9TH+A0coPH5+P!Zx|r#xGkhqzG1@fJ z_c4y(?Z@zgIE~~o5|MXA|41g5-hL7z?LecOCm0s00~f?P8|1mAE5F8aN9qX=bd-%~ z23EU#k8^e6&4SFl1Q4IFEMpqL^m!`?>}(jxnHSHqv?eQAiOZ65uHSO7x-_{(>cUhc z6&OUCu~~1J+0zB-W2L zOmfW+qQdfTJ#zBAQWkw{zQ3~pF^n&bb@Q1gp^|XYNNc#UxvSDlxp}o#y5Bf)dmXAM zma7Q6eC09OUZop@J3gF!`}6e{dEd5(~RiGBj%_3QPF3u@If73 zF>c^fjF-J?k(!bw_4TpCCAZ=S@l=#s?9&LbVi7qB5oHf9(gIbJphedK^CtgU0q*tV zwZTsv|A;W8M|f)>(L%sp+no85bMg?h15axpzD}L$0#WOy*}j)RgL^ccL1X@?M{{Nx z!n^~n@%77CSoqT%^FM-J4P3m0i!fd^2DxyoPk6~nWU#RgloZ@CkH2d581A?~-~BYP zC{XL?GN(6H_rCuubiDbAPM$WSn#|Rf-sw7%R$>(Dw;$+VPvntcI|;5}-4FlhV%^4x zfpiq$IFX*^DwDvtj;ufyFRsXNk#au?J?3`8fmj!>-rd1$BfxGhKjME7(sdIIJdY6v znz2f^*i6Dg+({v7&Q(7-VJ>2=*qU1^v=N6qDGN`@x zm)$hmMj2J-G>N;#w5)u%{mDR9h;HpK8Ee)&n*k7mBmBwRz;Qi-z%KcEjI*_sbHb3$ z!q7OYZ?Mj565VAsF@5Ged#lGC%V!syQdHBP%rMyBj>!uWLR$K20xm~Mw({5+6KfGf!se4mv0A?)R5)-K6L-Fi{|2XxlitQs*F~*x z+xm$P@Cl|@1hNi%N-@d_{I9MVzyF^hCcI>(GG7Lp*>=O@M8K&N@{8!YIe365n})n&LzyS@^xt@A0l4X+VPxpJsz z?>x;$oUF}r)fHAgvkGiE2lFDo0AEP%(vM;lFKyj&@5gB<+v0MMg{}}mATk9*U3#tA&05Utgz5YXU*9V&G)DR^1y8jjVMAii@W^s9L59Axq zhvkZ$SoSYX@#4=w>C9r!<_QPPQJiu*ERVhji8XQj*S5u)cPA{W4?Tm!|D(YrLCwvz z^4>$7&a2*m3waQN3#-W_(AT&=jG8!B|%hq?FtE)+6((z&hUn!U6KV z+t*q*c12>UfL-Y&zgL*^-v(g4vtFHw=i8SojBMu{wYTk}3xpHpb2dCF@O8d#y7>JuOu&MM`2HFs<%asG&y$YW3A}k`b8L3p3oS-JCxy3;P55iPW$Nbsc7j$X0@Y zNAkmSnKYiuak8&I27WVdeabD~nJR2e@pYswvN_NKP3s?sR=9o?dT3SIrhK|*=oOqD zS;YB7{&vYV0fF&XobqGezQuE2QIeJ}GBY<%@VpV3mBj_CJej7*3jdf&Bs{FS+7NX5 zM+LiNUYrK`v2?D1l~cL72kV}_cZM2nh1mj<=9QWCYGCR9*R_=Sq_ zo%h~*12^6C6aodFq#wjSTiP@onVQ;=xtrHVvly8G6+uNUQZxUd5pn|G*kXejkl~}N zoOE?{S=I|O$p%hNe21mf5O4o+>{zbopqHs$st)ziYs8!=8|loCxO|Os^Wb0>=wzBG zgZaz3fs6u)Iw$43X%uOxrPe#4=)TnyF~BD$4jC9HMXV!g!sV*JM|Ok)4kNl489U)h z3uF}i5rUYtDkSh!m){3P*%-DCz0~Z|w6d*k`y7FZ0x%sNfnF5^hK!ETRKa`<105kO zS|C6>!USO^#)2r(U73V~NG9DLw2BqQ6%GvSD`AU7{ByPD(SQa5Nw2SAox6-r1_#M{ z#Vm<*ct@o6(xcUN9i-{>`Di%K=agdz1i~-3Y!G)AIm2|aFljezy|@m*W!>7$f-W#y zMtJULVA7!r6&6~{IzedK8J;1sZ-)#KVy#@@j|zr7+cK0^N`+zCFZ;rv!Uik%^9cC3 zuU7c7{)hnOMLkq}rP@2XpO^bgXE`|T%J#N^dPB2q+nyKaTtDK_7x&1H_d?)^J)$)2 zBj_yaQ#Fyt&rRB|#rV{Do-T!lSyql*vi_WEcCRYL2s0@EtIf-WRem z0^V68b#-`L6}+pp-AvGd`DNbfw^S%=F?1FT_n1H4CKSuQXZnjU9!j}-z3=&$`NeEY z3Z`EcXbBu2GHtcSF^3CF{x%9_SiQ&|a$`zJtHKevfmh>>zo<%9!yL`xe_#SASsozQnNyC>RW>E%GwC>Y`-Y|hW4 zFlq6W?%?2ha+fH&_~(7BpBe4rHn`T&xhX2XrZt$}W{wBuF5iO<838r}`dRg9g2(s> zoR%&Bfh6bA*U{bk+%*Kd)W0>kMPUY-A<}aR7Qm3Wn~nw*J82RlC4qGZ$XbkwB3-#w zfo{i;)K^P!uPt{7zw)yF=?J91-{nHI8!tdnNlXdNm1PJ2KDt1?uH;F{&a;V%>+ERX2HEIi-k& z4VaHdTmZ-8MFJVPBv)I(5<@S|TMvWKL9dT{79O8>ho%~Qy2@lz`{GmNH+WY=Y_4b; zyz{qtM9xT~ufvz-G_U36_F{2g($2$rIMHY4pcdPy1LYiEbuLE9+M{ad z74+Btu{`SP>HuaPuMTvn>M52q@*VZ-4o4IJG=>(e?DzG$NRx4jWu06xwBNqE7Gd*O z21IQ0s*Z;V+VEHt44-=oeK-lEX8rBPtNqWN5V{odG8)~4oFg6}s}ff{MQavRaMLR3 z{*tBH`fZ$`l?73`hsBIEj}W+Juz$igXE@g$v+^Xy)8==ZdUWcQIquVwM#D)O1{+IZ zY_W0BZS9Q2@X4T5uZ5}{-!7V`KPKP4k|qF5U!0Gu z8b3U2u@A5e` zqCnhvG*?6CO{yoYD7N#~h?}xcT-d$7RJLF(8l2up%>k+Q82&M@vMRy?6==h8-tDs5 zpW74@MP&ft6Q)rXYv2J&Z|PJ2kJ46A0k94Gz{2JRWp5&AQ5jvaRk?x#B34{^FqvoA zHI+_JW_NSmj6Z5q4G1Y6mK2E*?D>)G>R^L&i#EN$&%gbW{iSY@Y+Q*Ld-TeGX;dc) z_1IGR!dh=t=3HM{>r^9dMhcK(UJzva> z43TSZ%Vb3<3g1<_z(PNF?PkQ?8N*+t%z*5IvSfPI(}~X(zK%r4>?=BF= zU8Bxwt8nYzeQRH7{~ui{DY*%BNz1BiEf%`_EzSx=4;L<3ddpzRB~~B&Wlh9J%!Toa z?nD*;z^-P}2_Z5$NWnwojFax)B*~U7B;lrxQm?n{HLiBFZRm3tzjR_0qm!iF%WLnC zfZVh?I_om*>rdNZefmJda^V{KzN>|T?2*D*qjSWn6nO=9?DSWDQNu<=a^;R3T<*Zr z1+cDmv|HRj0-ND4!mPx;p*2umx5Ir_29HZ-+g&@ddFmOw0kN#ykT&p=$RP0rYCQmc z#ri$2giHVEsPxlto#SbfM_qlLs$OFG;jzmO`_brR-HVb;_G=V9P_?@d&%BwuDF24y zi@J{w7Ji18Jo}SSvc7ow;CL8yZ%#m|qG=F-&@9j1@SJ%0qoVb9eTMyhb(E}caFq)!*=bIBG=)Yq1Zgm+v7gLm38zl?nf zj~*{-4b5Aj!?n*S6yJ{4FBvE|@Krfg8u#Au?1t&nz8ML+@O0cbvMzp2G+3NcJzDZ9 z$SVE~g}CXi^rYXt#aOhr$GNy$l#s{4ox!h(>KU`?@-MiJ(vKHDPH+b0TNlkimE7Jn z@LXM+4fz@2hr6C%A$s+{nP9n^`BL&%tU%dRM%r3C)>q%6V?@&oNK`oaUa~%v{Q(ak z@M*79x$lXrOqLGpVBs1b#8cy+<8`hdB9_e5WWwjyw>}u8lf(y)W!9XI7pWQ)hZ=E9 z^cm8Aj@oiGij#}W<>-7LMtrH~5NdLQP=Vhkk3MnV)gPBXB&{qwG|kT;$^DgaT5A^Y z>$ju-f>bqe8gA6`^n6+QJji`NiYjAy$igz7J13|DOSwhlx-(N0MUCpHdEUjxFRYOH z3|2V(qInu`d9dJ?QfAdM{Z~3-55V{1*_(#u#(u?ZnOHr}Z(qtiFY#Fjh*x2X(9X33 za+jXiTMzHf0&)rYabLaJpZRH>XBoEN*>HkyGnY9jIndG^#NAXj1wN4@w0Vmwo~m;k z-vanrJYV0uE9at}OPkkP1XamqSHe`=_N}fpygjpa$Tan~N&B~7*NF;(HKNXDH9;xewwUWgL zTRt~FCA`hr{HstTyOw@pEbGv5?=M(}*SsR+>ylME znDIdKIQuTqj8##SXkeS*Rk(?lFKWeGbD`kHzyQfhc3^c?s-Y%TBgtGRT%*>s-%w=`y49z^GKt;P!QoB5t~B5|*o+RCD9N(C;>_NlT5$ z@D%(`{9SBquiXh#7f)~yO@zxVO*CoBLJ;_1+@t-e*_ z-uR}VLIu*O)bz##5xOlCX0Gw-gdrc>K>>*K1mnMlduq%TOBPg6q{@42IcB)wNe$l+ zd?}-u`+Q+Gew|N%1n!-MZ+7gu=?B6T&?hzB0LxiyJI+SR$npbZnGtgZBem^r^LR3b zi2lqJj4`|Slib(9N!M(>rFK5Qg##3coT8%cUW@(>k4mX?^_5V77V+`$40>izvVXhg zu9hWJgpIRz-Tld1uU`^a1I3lpJ;TkT{*(@Q9&Ad$t3q+Zv)l^C$c)Pq-F+=1|8G*z zUZkCfZamMn!YS(tFEL!O=3-H<(W@LugZS-7CO!UU?w_D^_y2Tr>@ z46NQp;zp#$i^ojFyj(*H72$UPl9X}1F6(;+VqR8}4l=4?U5KvS^6}-LLgo#u0O4eW z_@s}iDOnUH!pn(%x{F)a|t4)-013GkXQ})8AI|vw5DqlpSXg6;)LFHD)t($yc zmD`du+!AV%*T3g~jNgWYB~c5_Jf-~0M3Wh8S?G=70dEcgZ=k58YlY`S*({S#VqVS< z#}`?-m%>XntXN_2k5L72rx*u|$3JU$9uK}OB3Bi#)Vhkkxob@OSs;cR+w5lf;D7hP=I7PIpZ5)ppDO(cYr?xM7~07Mb;%mg-bj`u0`7ll@ZAZ(gwyEFWJ^a( zXdDj6u&x`kF*JlcINyg|m^3=m{`@$9ZaE&5?8gZ&mRv4_{1!&*)$HDMy$V+Gv;T4E z`mbfd{M9S1m7m;#c8x8b@ioxkXW~kBKZvnU3dzVc@AzR(Lr%kRXOqFXt%n`>2QYl$ z0d^jOFIB=Uz>IgX8<4F%omSfn0ky+Ox3j!Gd@J@nUSb4F^xr@*cR-s!Tel-O~P*2bP62o??y&ibG5btgnT(r`Wb z?BBH;WDs;#1ADN5yQmy92)=N_=v3z7gLWeiW5q5k6pwScOGJEv$7N|O4!73NJ%f+l zT9+>P?jFZ`L9LEkccH<}J=tePuSEmzWQKG#P&_&6TQGPy0D+$lC;qP|p`lKwU*-w8 zKe1wG-=I%|g+1n+vW#JY)h1Qhe)!{rmCvM9;n`=uCe^XZa~FO1vyldW{EASP)EOY= z_vmz4CgfB7Jg3Dv$=gC6^nc@pJnuxY$e0N}DUuF1m!SOER`7JFdlb^bMzY1H%+ag}E&xadJf^Bw#FHk-AFyiJ${6!l*o z80Wguv)STV$^hoIg`h2g3)uR0)>L^*k5hjEhtkLYu_%DLaQ3M>O@dZc{LEfs?_r^Ru~j(iy*8<@1}R` zv{%r>!Vf{D7WOWChc`g+kBX>NZXde`LYOhp_0|DQM0vBklc~09OBdJk_HwupA))mutIcj>C#8$X4Eo+MvS#)XfnR209za8R_by zqi(TMgNx%q2%gcI);i`U>;*46M`(H{(5^O}yj_bO7v$%EJ%&J&kFZ%ha5oa`pozk) z!z4#6{Vq-qSA^%W#y{}X5>K=j*0?;Ni#1~S6qpv|Y;v-r$2)%R=m};ThHHhHFr0ti zD>^?~NBtQ*AA+?Mi?;U7%|we<)A;W)XT*t$SU`A%pO4L19?y`qfGmj{0tQjPvnoHq zaHrg-31Bwg?m^zD3jAm5$_orEWG#T&{VWc4S_e}i&2P_FlhQ)$E9~){4blV`2JiP< z$Ph3)`kj*n-<1~AvOoq;7u$olzU&JH!?(@(W5s;ehz?z=Q(Jp}M_%NH1RI`XQLV*R zhnj~w!cbe~$csG`UM=X9tnnq{CB-1x&QE7r9kH*1Z9)09RjQ?ZiD})g43;;R8)%s= z`{^GEp1#;xcwQl;h;M+M!h*A<52E^_wQDx1J<)v~ZunQQabnNw9+XzH*1;F4TQnCt zBq5PkgB8fcq86~diD$2iWTBJF{OV6yep9tE%@t-GE1r2{zVEIP6Rn>8++_^7@dt*Z zT6i0pO0g5y`rXa9$IdpEDDkNb{w<`OD%gsDuiyInF+Sx{U3GYfZQ(`RKi&+&2dZ+Gy{pjT1ip7~JVXq`%r=)HJ9}kT(>%m*l@*nD;>~A1 zsP(u|m(zay>7xRIg^jwa!~V4{`0WR?4S4lx?>7M3)<1l2i`~M{ORv@pkM&$SygrNs zJBiqtfwK)t+qBvpb4Zx&rfF3_t5uyWJk|6YFA=X0?vTF*L#}?Pr&9g7_R$HeYI!AGjz7Q>4tm zlF`@Jlke8VudYn)Qt?GfHygMw7z9kNV7OBL=texScD*zypse1>qjv?Z5Ase3?~|Im zt!>jIGJfOITA3hj>-&>);re~9`y4SM#H{&c64P(ixZA(0vqNok6Lb3FS3)Lh!fb!! z^2a~CCM-KWzn^+2CS8koEz?rTp083(gEsq#)r7EmURyXFhK?~m%L$tPf@M_&efg zpOk&<2vGKfZ{iV7&A(a}4lRRymuyw%KUYOG{u@{P`FHHXHRid+O;H=rE9FFC<9ah}YA+pcRN2;hnzlcn+A^emyO%=KGM@^i zhy36-7b*m8*JLkqomVCa?1QRt8^N$15f>a&JUDUPBRhBQZ#xK#-pm4V{Nr`1V6Ye^ z1X+(+$qGF7iDSds3+_3ITm@Fx`xyjwIV5-i8`b{xY}m08#sI%>1iQN24g0g1T}&1t zWCp`g{4!E`{(QE9Pg7wg0yJ^Ota55^C6cLsP@QCPp3o8j=mL6Z2;Bq{ z>XihMuGP7O*+Hh&<^m-&CWA1wKIsQU6%NyjdEG%+iH(_Ds<_k*2VZ7f&|)FWPe%>Y zkS(3Y12lyAxIofe&U3$ujPS%86G&3nfzh|3IC{T-bZ<$;DLmVlT-nRvbmZ`lQm6P( z@o+nw%&qtnJnNeCnBmWE8v?=LG~T@)w3$XpYiXvyHQfvDOy4|au%+ECM#V( zk+v8d9gA(LjeXGxp^g!2iaL=Xo{zk*c~_?L;`{RH_Hnm|t(7>-wc1>E!Ht&syO*f1 z2O;HlDF=H4tqzHpyU8PI8HuthNK5V@P4|U3J(eNI0ukqz3}yrVad$(Mkb2c^Dv)jS zNS5|1UEQaX%V2Q}px72dD!z}foit^r|5d}ArTIcCi2U9m_-Ir`t9mnkG`x>B{NCO& zIx02HrG$I}UuYM`MomDa?ra~pU;DqFFLsr>`RDg0!*Em0$F-|Y1q>ml- z@Wnk*YjUbVZsY(e$3(wl8{~Cc>{*PMH?OkE($4ZE?~&3AbG&KFB`5R|J29-1 z9$~U&Nwjc2Tsh|3i8mk%ipTvqA{5rVgRVQRJvLVk|B&dt$R3Qcr#x`V;m(lWS*;G` zm;V&7rn8{ErcU6$aAW>~f+ZAQ*8hFf+s0)yO35jUiKR5piUrqBg9X;XHRehQ)Mxp4;Y}7u!!& zffZ0nWan3EHwnv*Pm!)wKQSp@9H6zJQo}_OlIjR`h!e(h+P}>Lv1xj-0DWtk=C8EB zJgVudA;qz{^--PGhQyYQb#_47N!EqAhJdK~8-^<3KD2>%_8qXs&Yweg6CLAIAd$utPzr? zT~TL$YP@%Z+&GS!$^PiMr%;=0XZAtppTmdq&|UltF<#>-lg(L;)>dPWh#GMw)Zn{N zI@lfFVY$yd?FCxPC$*FbdTcvQc$PE?4)Y$=)IUgoxMv)IRR#ro_%#}52yn|M_aPQ! zar-N`yy{zbB~1VY<%@_Qq7yRMMe?{ytO3oVPKkv*5Sj7*bs2k>oKOQxy4tq<0-3QY zC9hBBL%r6L^b)?b=by#R^0tgW5thL;O`z|EJa(wWX zJc(wd3hY3)RQge|`}*JAm0;#|z7WtDF)jf!*bi-{&oA^rq1vBwcT+2(pR0%5z&~PG z5Kn?6fTp#XY|iCQ0s(Psu*a=22eEk`l zlsAbeFdFu3huwaDdAu>7BJGRw1{84vyEracFQLu;rIZ_JoM#M)0?no0i=YXvP!5kb zXw%yU!O2_H*}BsKM)Yk+0%;w&Ne0}+AN2;nKToD?eydv&Er%<~>}c!hAnp_l(f5G0 zv-GD(ZHK}7M|J9$7A4HiF7{!xl8rW?bH_t`pvzvin?v&8?u(PTNK>oYH3{5CScM^l zjyeLgc4T$>VSPLr|GTT*9+N|bGW}-SvrF?NF{-#V*N_~(K4}1H(QlLtlS})cmacTu ziW|%%{b7Mbw`)`;{^qqoGg=3VDkTzfyg(LLs_Zf874#D{o`tB?H>L-hVft{x+P;mD zNkI`z&#T+jMj<`*j6pnz?cP+ea(rvOo@)CemRb@$c^)1X$;qUU7Jd=XwKyf`&Z}%7 zNt*-+AW4C|jo@hIZ1?zZ~?#p=-4Lcskmo#KVZ|z4>kofg{ zss!I)D_UQTZ!09YzUNf~4iK}#m-z;&Hy$7`H2P@sucP6}tp^JSn!-dErW5}=G~IM{x4^*Z-aF6zv~+H8|z5?%7} zjIDSU2X#8pmevx`QO^$7nmQQdO=T*w=M0Yhp}juXPQrra;Z?H(o$sEprDdswpA01y z9}h;aGNgzw13GM#vT6?@WivS>ls2#J#F?dMWqt?Sq!V1CfzWxEnR@KrVG0{OMSR)R zq}`r)!<<7#sPjW`=~R^&Q{m>}*nYrshcdX0{V02H6VuDatuqDApRw}-Q-JSCo?#-T zEY^HNv_1PhhvPln+<$=G`2&BlmoFp+U7Pe*%^sS5XYGCm9;eTJh6uK;)t30h&3C0* z_ez;i5SseNwKdGJRC@$yaws*glZX48n|d~*l7{l+_{Xt5iUjNn93!a(Q9i#dr(MCP zRu+YklUsyl1w_2e$I4mZDS1D!lfx*kNYDco*|1h8WZb?C+bv&-;SD`eG{LG~P`xj+ z*$0Nfut*ER>?vP6-W%CfqOHj=?k5!nrX8~$W?lrP?zZoBEQs)AM>WIRW>XfV-@BIE zLr5vyy}j!`q@@YQ?2e7S+fu;$9iH)%cGg>5DI2svq>|f_o=CulGW?zGllEOXgAyDh zXWQ56?8?HF@=UG02(yT$hho;&xAsD93n|2h)|Z!CipKc~l{*`9r?JW)E-o(7m|dVq=6yg{Mtyu-xH{H-dPS8Qc9D=*_;eT??j&a)N@bFk zhI;8r5fpxR&ragxZnjLkE9=fM+)^$70Sd;Ai1F8B4?Zr21)hN(?yDHbw=#q3C5c%M z;~lPnq`hwK^FP|>aR0I$MbRR!-RPrEvG+AZ>S#3T3qb&MRfhg$TOy^Hy)Y%U6F3=? z7u(8LOUVnamrE3FM@(E17BXo@ikfw@OoawNDwxrLZ=)Y|-R?S)Q?Vcuy^gG{nx=(W zk%>Wt`N?(C)dXXKx)GvX{q%zb+XOq0kwx~O1_VK*ES)3T zEoFPCF_Ppy+EoOUqK|e$;oxP|z1d(DsQ#ATN6%BGalNdZE8*-h9uG`)|C|vN^%7x3EVquA1|3^|VBfU`p4JV~JD!ZS)U!X2t{comX^1G;NYfb31{miN zsQ#z6Jwe1R`8Nu=0mIm;A{-W(%50EX4)|3Q<8U>WLo2L@145JnnPWTGb_m+0N9$2m zf{979v7H3k!4*^-r@zQT7HJ68CMhb&oY=wzY~hFS*KqVWi zoh;R%v`zL^L5E*Z_uu-En45+r@&6ZSa$ia5iDxGdyUW#0jcTK{IXYEA$hgg0)Z(94 zmVV&^0(WIBeXd@1)9R9GW zA}~seKpD&Siw_m7E0^J9qW|#?N-B&pnW|W#c`RO7oES*8xOCGlhK7c=3vT2|eku*C z;>nrM{rf;s^n=v(4Q;rIJ*oc1`|F>%0DbpJKlBD!kslg})!W+IXPy$FE>8vOrm#1( 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 bqzh*7gh6F%F2Ioh__?F5uU4UA6ZU@r1HX_2 literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_now_playing.png b/app/screenshots/sidetracks_now_playing.png new file mode 100644 index 0000000000000000000000000000000000000000..267b29b801aa9dee50c20d6b383d5e286429dca1 GIT binary patch literal 47123 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 NQ;<=8S`LBw{}=gmde;B| literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_queue.png b/app/screenshots/sidetracks_queue.png new file mode 100644 index 0000000000000000000000000000000000000000..bddfb4d776881ab865da473b3bea5021b269c707 GIT binary patch literal 50727 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�UklI$5kH6?WuE1AZLT3M6)PYlBVy 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_search.png b/app/screenshots/sidetracks_search.png new file mode 100644 index 0000000000000000000000000000000000000000..43f58192a17c214e37af3f7d46b16f2af71167e8 GIT binary patch literal 45126 zcmeFZXH-*N+b&8d5_*#&Rf+=Am0lH)qEbYq_aY#@2SSNb1t|&$N-v6tbO<#N=^#x| z5?bg*TBwQiz4AQY`|UmUI6uxfdyMnv9RCQ(WX(D6KJR&5>$Rc2HVq{^B@q!3jjoQS z5fKq+o`{GT2EGD(qS;pld;lZT)x7rKlr2Q)pdf8BNSiKtAlwt=*TjG|4WIk z+5*276C1)Pc%xD%?}Bau!)_7%|6l%}2|+%lmnUN8^tAmFik-VWpF?0n*OaWawPRCb z0jUdLrSv9cm9Rd%z+PUA)J9-J5A+0WZXl^%o*(Q4wNLkJfO#DcG(bu77IP=YI?KBU zUY9s8{k8`L^`vZr$mEGBB>{CM#t?IU4?tP1QQy-@3ut>_=n3+4b=^}vog9f_V`ul> zXHW<0l9S2Dl;nj^o&GV1ctMJ7nLDpQ{rpa3O$OAmk-w@Wo zMzDxU?*F?#xr=}*N=x@D5IA#e7#5m`e{B%j0X;uLAq4Y6$CMzu`Kdug-*H^OR#q}> za2UuE4c39@G!s8~X9OZ>kn6wynKzFJSkQ;m)(5}I|CyT&o<`)hH`(BoryGmDHy@6x z&5Pc`jm(`5%|Q&5_oALW=RC1CUti43%DMxMiMTk(i>+vg2tQlmfl43_-v!&HYDTA-GDhvb!rt6^i?YFXA~D zN_)S&R(W|+iO9|6E;+Nr4+tW6kyOXW3v?0dG}P2rMUnW~ARFCN81W-8?~2_+s@=TH zmOFBdNZJYo#It91m*Ct_0X>#6!g z#>PEU`lKW~)F+ipPaJ%r}sgt@{sz=vpUVzornh5f8*-JXkdU z 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 RUV(rg83}pua#6#;{{@~(k~9DS literal 0 HcmV?d00001 diff --git a/app/screenshots/sidetracks_sign_in.png b/app/screenshots/sidetracks_sign_in.png new file mode 100644 index 0000000000000000000000000000000000000000..43d04a029efa0b9effc2045e2505afd8c0c4cfb7 GIT binary patch literal 33859 zcmeFZby!wkw>C;iC@q}=(%lUr0xBh-gfvJ=mvks8DbkHJ(jwiBG=g+@!$UV`F8sZF zzx(^nx4-LL=bY<0f1Lkdt@X?`W6Uw{agTef@Fz%Dwhl7K|c=7z1G8`QIJRBT? zH!3o?@~kT#T=0f_@l4{iv(9#Mz!-~Ed1Yhc0twDzv=$t6??-__-j5{azu5N^KSudQ zj2MLb7?pu(HOU@xEQw}}tXNQ6#cRuKjN)W_ce5yE!(}ZwUbpbLzx30C!ZLAh3YTa`0#+(9L`Q|IPnf zld$^KoAP2XfFOYUgF5VYY{O6I+sip!_v3tB)`rviJ^a%-FE7{%yI1wuc&C`^wEci+ z`!D)bt%Ax~*XT1rEq7RZyQ0^{@FJGinItTxvMx*AX~vOR{~3OCBKFe}?qP(GIO>|l zvv&3%tn&X@-d%07r|z>ZYj1uRF3DTFs)XoV-JCWS`#x;Vo%Y41?vuN}|3^(%n^+@s z)2i|MaZOj5Ni%KG?Lj(E00uWoy}2UyzY z*}cwXLsVkBj$Oj^1C^V|+Hrj0rh@e4a#xufuZW~f+UY%4nlD{9c-E$dm3kv0* zt-=-i@ry1IkMd?e!0>3%+UruY^mtTLt;LP4@#md958$(VcD%!q9P3RkLhlEZgZMw^pHsh#pCK#lsR`2&{RBXx3R=tOPsq}ef9SC ztW&%j=L0%3^vtJt9KBJy48wYU{~kZl<{@q81;1z;Xn#D8BZe1SiBd_|AxN~UF{9OD z%P;ux(n$3DxY)~sHVN7+XvWgs>^1A@RHbc&HJjg~ZM4s}O~i$xW@W?VV-!CX>@OjE8R-0*|#@laqYVPI)o`1$cg;q|tpnUXg`$7Pvj+WQ;al2*ZjB3if zV>`YYp4)b*46Q{f$M3b80k_%1AmQDrj4Ci6!Lsc2KgYp3FzLr~AKFnqMb!uIuphN| zAY&wMmA_vxICbN?ly}IA04XRc?fToA|xoMhs+F*|`SYid=(fB7LDLqnt zx>&vaqld}G%1-z8Vl0@oVmv)WI1J9Z{EuoD!%EG7$F9A`Wv&d>D*GEN&S|?^EGpmi zL@>|uvNA)qzi~TM5-lqi7% z%Es#*gZ3c&=%Y^B|JoL=-st5Gu-Z<(N{0}hx$}M}f=&vz?ePu`N2pP>56=iPmgIbqC#xwCpY~s!zyr)CBR~y@p>^37IXOI|n<_#dfEBEN0r{bf%rZ0T%j= zV|5l5=JfhEWo%sI>*{u!o*gk?>~w46m)`KLS$v(Y%mjTz+acz;Mt5(Ld3$p{;6-si z*lkO5E+L6R+vE68u%=aYmI&^!*0$;L{Sv{^=yNhXbn6nV1S}foPKy>Dx1Bd=!kC<1 zd3M1Iy{xg9z&kg@3&JChs@AKQ3yNuW~yUOZj^>1a(`PISvY)2UXocke{$6iee=k+JTIz5JlYAtx5bQ zSS$ZFr~%K1DY*RQ!2Ee$gkj_*@N2KP%Lc@vdA0%(l;|I`yAB2Ww|vPd%5k(+;Prnt zv~r&#rioMTq2pBT1&?X)c|LPx&3{a!j&~>q|8$AK?bTthQ~j3q$~yhn^#Gx@_mV;;~H!1@*h-A?@FRlT^L*48*lx|hVWs|f9;?LR81)|qMJ%9QT8)v zZ^UoU2YMb>8juAd;Q0AkvJZvYKYDic>RAz7r~3-Wi-qn!U8^cOI+wf6_kg|$S#`uQ zLtpFyyY*?}lV>!KMrjO5w9lGQ#5kf-kgAej%m!dTJ?d+@&tVfN>0n{w-R*Sl_IfUwV~`Fz;ZqSkzgP7_ zLfoOUSBx-3VVmsX?sQ5X@)i|krEVs|BrkhbGchuaD}rYd%*eB`t9fArk($6 zej&E;YUS0^#b-tLbX;RRfxPb8V)qs1XpS#5*tVp-xpTQIXYERrRDV|TLtoriv%0Zs z<>mbK*T9J3ZSQ8AZirR{IIjF<*AIUrtyWUX5@>}pNBBDcSh{8+$ z&tdnX?(>dmagS6j3S4mdZ?z1kocvsboYJcVcK3w`%nRVxfm`GkAmSyq-SoKn~2 zq`1C^)$e(*(yIza`5$hhNx9~Ljso;@Eyb0De89}ia(50_RE<)PkubQW7~73!g!1#2Iw4qO0`qL&SKK1JWbZLj_mQ(!&K zhe?033M`<18{yUtku(4n7Buj?@$8i4n3AR_ct+^&I0U}p#c<$}y zf0&oiifRu0Ua@be1$ck^_30dCM)G8)CEBw!*uP%zm% z)#HRw=Xz6Z%u3?|;2fQ5e{i}?$RLQohXDX_(2B_z;Kx13fAgo<^G{XyIRqg?ib0bK z5paru!wSNMwtLozOOd|vCMuvWah8vp9=^*jtYDf2XDnZse}>^jLf&Q)uOYxTWx+_! zX!F9&W4c}*=MsUCyIsjsl#@iH*Re&S^?z%U;$nQ>o>-R`+KN_czo1(tOJvOQ?DL`@t$cXuY6@bb4cz03Oebf7spE{n+ z#M7w|&I1otwp}W6{!6h4F9E>HA$n7cgw}O|%^_uOUU(hz!t(BoWpx1vlPY^7f0I&5 zjR~S9oQE)Gdv>DLTShRW*ajpVQlWfO^$9$(bWQn1HSb|<^kSuUOVF?E;Aq%>GHs4! zs^h}z0F)V=Bf4W2orn+a#XK~{Pr!Gyz?FOL4J{xcspU_l6=Y~tR?sK?{H3(FB0r<; zw^H8N5Nn`;mD3n3d9h2A5(rloSJ=u0nY$1`J1cGIKV3nW#4m>4?tykW`P0lyHAiY~ zH{N)w>3TSRE(Q%&BU)}6bqOpczFFs`mvm5XT#)*L-wNC}e6a6NYNh_`4t@_7$bLJH7@DRCy-UlHPAzEyJ+V1FZfY<97F<(( z=nAf$m>wB7c;+b!wW-0#FEVgt=Ny`(8^i=6N3MW=gt~sAt=DX9&?8*t#q}Sg*r(Q6 zn(n{^A!ObJ#6LMVjYyM`;#7(qX3L-u0ezB)w=7fvA*9-|E*D&&s$f#pv@8#@;v0J5 zE$GUu^9ou$O|Y2@a@UO?CL^hj4uGegiE0;qE4$|${YEuUOAdMj%Uf0YS}uaMDNeof z0ayv_Mfe&EnQnr45E2yIQ9=C{fqP4Q%q>*`*8?XDeqx&4y`C>)a_kt8_JRd!-1gox3hN1Lumy)sD3n zv*&q_EK$y)>(*_s zDr;KN^B{8h!RkTpl-}o@HIsfy1EFK_C$B@o~GI9BWXmJ1H+_)j980BS4{W)}t-J zK72khfCr*05U*Y$vmZl%YVqf=LcZ_ER!rdn_l-WPx5C>clC)a7&%JBQP#1rqp2jWa zh+mtz%z;NKNn$^N4FvTb4c<6tT)Lue1cl7z32lf;PVxt)v4Fl(IY=f{%ue~u0EYrD<0OjR)C)14w75r}hKfQNx)A3t> z3H6ey2?{}xLnolfHOPw`%!{?)TYo6hlk}_(rCAuyKDyrXYSd!yC_$1n7lNlxb+;p? zQ}c#ENQ}ARGD<7^XpY7(#6ak-^7^kOSoVj!y}G_b8Xo`|vB+xeA`8pcz5ZUOIwSg3 zZFIm3oNM?H&(LkN9J+BC+i;XMp%MVSv8UXrOq!lG230*$d@+!QX@RQpioJUI>+c$J znMsH5b>gxQfc2!EGlpZXP^gWv(Quf&SCC04c(I;=A{`7NOO(b_?GKTjI#goj zBdyTZ|3MZea?thCIipB{@f6ErT{0`0&#~LBkBd-NU2Hd6O-G{v3S_N&PH%y|IG*3| z*KaXid>76LKX@Kp-t3W(QF!Q?F}g&Jeazi1hsxpqdbPxg!bzTg4L)lmfzlcT_M$Yc5`Mp!SzS3*nYLrjkraP}&6)}%8e4ocSx&Z-ihSqg#?w%a4$_yH@ zMY7^Q&nO*HJYr-vCwOX5Z8SyMaQ~XyVcKs=UK#f5%t(drzCBqx@FHjPK0Sm()0BXV zS%eOk^QUz?me!;^e0StbN?tWX(-CU@3SS4KNL#T>pLw@~saD@m?FqH}Mpi5VyL0n{ zm!`|MSLTf$LmRHHIk;MfT%5;hXW!JpLakYL8!mSxYAWYEFCVe&g59MPPcOYldA%+Z zKtd<2f8&WhI)3e;B-Z9Jv!$b^bEpZTf~Ihat6(sl-_9&oDBCNGs9@$-4thk?#HP>Has|qkJCz57G@wK zN`G>6&@Eg@$$gTmA5M?h{Tm}txD~HkhU1r_$mpjknH#mhD1d4u9 zSRI-(-9BKivw20a%o)V5<$1p1a~Akwy0Z_pS+hkKAd6@o<+dO1YDrNlNBlD2`8&&q zuKU{E$SNt$8mq(ImuaG_kcIE{#*?=_?&)9Oa`8(t33cjpWsI_R3SW~-ZvjJP$qe1cCe0`&bNMZ^L@-VAz5;*YbM(A9BP#H@pm3->f~q| zNfaFA$!KgINe~=$sH4RAIx{xlb#pk0>C;SMZr||jWwaOGTx{JkZ8T4F>N{0_A!Oa~ zqpl3Epz++281hykhYKRxIKyR~SOxcvi(HgMoW*LB*LX_^1IdD;!{?S?jZBV?>pr-Q zuII)FOUITfZ1p3JiCM|sQK0`MaxiC+n ziDPNfyqNWK2OWhp;%c1hsb|CRN2M1bTMG^G62K2Pa5&7r&_36-X}*UDGaAvT{9xg1 zNRVv@=Aed?+WzKzP3=ngtZwKDAFth$CDZ<3vl&qmj}b(2M0;D0=_XTj86WFL`5)UM z4Jl)610);kM7ejM16L-85o}5nAw8-#euq zLm=Uy9#%##;8OFnTJ=N2{Dp-^EfT!S7OYSQ6$trOq z&!$`At>H11OO?6RT5smMzu!S$z<$p0LD#`XHo!urpjl`DsC~@tp?A6($Y<>*S)OQoa9-_tCsl4nKeD9IG z>T7-+>^Cf~0|*-~uf|P7cs==dbu0&ri{SYGEK%b^-uZzIl0?TkoDad?BAyNCcn4B7 zJ3)r6y^gp1t6CwQA_3MvSrWmgk&9m>F&RT26KM>MtG7PXwm92q;E?`owu&qBcy>N8 zKA5NhGdt&+LJgS3mQ08@lQ#)Xyu4)BgN%A(YOmHH(rdf~(9byW1FQ2-_|bJvw~rMh57< zO2Oy6q5>6Sk?%#QMaEa_;N0?eir5rnPyVFAyS$5{#OUe;a7`JP^rmP9oUqo(J-ikR z-UPc@R;!52AB1$0s3?|r7Y;jmpB)vq7Bp}=m(ERYzMFQ1vwpFY6G=RjfbaEPlKb-y z?h99)S}}9EjYPxX*y|KGQFSJGimcsiB?{d6p}>oH13IL*fPn6Rq~q5<0WDfqSS4G* z(IoJiLPU1$0@@q-5{5rlD%DDlKgT9@#N=H8bXb}6;dG8)mEw0#Cg{?LOjaN*K_QI4l3lIZDrY6~eVN4;?)zo>vm zA-vaytS*z6;p?B*CsS&Rj^B!r`!rR#-ple96-XomV~8Wa5g^&D@}nh8)pb4o@E!{z zOmLLm!{x)r3Cs%TI73%n{}&5~F||d<5X*wDh-lWx`{xDav7S18;j}`6>9cZBRHAzDn%+EP3#$&kL7rmoZTC+#)36U2 zHPFIpdAasW?1f1q|H_z?DMvwYC^koV$FC=jMk}Sq?j&XFu`O$%3QwjQq$?=md&)oU zW>BDu8>Cy6P?lge(~H4AzdU;plRPwbIJu&M_)yBr=H9XnR*bTD?Wnfy3Va1t{f^i* zZX%LE>ff{Kx|7J+CZS)DH|~3diupR{g@8O(SgPGOocEI`A$qQB_lTCzwv2wnj5$=W z1(3((PdQDf`51GfS*k`gv_2&Dblt45`t?g^NUzrrC1T!gEG>?8{?!9}N7eKzfAgtD zBY!qub0)9w0n#v~XXr~Xm%bU4X&F%>2LwxNk&Xa$-o&NBP$JL&aLHFX7YsVpV?J4U z^F@whYA)UrgnE>%U!NcGH(dNF#{3-txAyQa7%QP|BQsYao5XG+Nbn)Tw`c92@jrQFkY z67PsNNf8_Yq%b|@G$DIzvS<&Z@aiUsXSR`L5Uvc$r+)vLSW0}mdV!hvNZoARzUO{i zi^1xvG6Qq5%m$VN@MD$!y;?IH28`~r;{@+8h>Z-5@CT5LC->VPD#~R)+$$}1WOh;<{XOP&nCpEl#(H_$3F6o%ZTc%DPz*M09JB$IH4BD_C9JExqE4rZ}itmSZ$ zpv$&Btpe9a!vV$#>4vL)sV`XJ`{|*B5?9X$C92qKP}8KwKLEp-_zXj`@XV1!ao0yu zAS92-h32cA;9(QjeO^w}hkAM#7_Ee(DgsB|9T*b&2}zE>lXxvheb;9$B$=f_(k*Lo z9);ygbr@@h)`90qc^sE?j2RNHeLS;yRwvsK>6AW5&mmZ}nS>rZeVm)< zJRX?SbDT}pq`lQoR4yV?r}#5gWMF$dEtv+xGh=!@+xTCW$k+suvl^DK>!@_!Pb#q} zzIHm40Tv2KL9#NYCS&?0hTMMBS1h{!ex0F7?%{9e0p*l-uU9jD^Z+XX^3bL?9qzJ_ zF;CGZXyx;!mDuNt_@_Y5V5o`|7dgX`-XhS$(!R8CZQzn#ljsFvWgvN2h{J~uOJ!{% zsoYSPEp-QNiT|suuW;P1>9EyD{v#;lZ3mK${I|f_{v|G50f4!Gr;r$;`TIB^O(_0( z9Z0}HvHTsUnJ)A9HGpvu1(3n-j_+KDXo@>w#k26cCR3=OoKiFYN8RJ^YHzLp!6kR+ zDV4nk#4fiN$@iRP&Xp`KiFhf$t{SeoPi{%EyDF8=e3*K(*e3*%G6VBbbh|DCqNfGE zAG!MlGFVU)CaK7F3(ESx`#uB=A~YlbB@Z`>ARfdAu{ZIK5UGBc4CQEiE@jd7+X;2G zu0YK0L<^(jw>AR(?SYsckkLMWbqvU6aqK9NE7Dydd`5&e2gsjxD0#jJ(d6?}2?E*g ziCH^;#xRQm?!DXn(E125JFR-H@#eU=9R%^4LE`R!@u8T$9cwr#Uo0>7u!ME1HT)2O zGIsjy2E!+HXjIZPkg4O$1670vqW<{;mX~_che@+&j;7{S)NnzPSn?0!-*Bx2f6Mrs z4X57xl=DHHTeBt6Ig+6qNW@P6hnJ8mpX?&g7ld-5`Xa(RKUJ#5EW&sfh>W4%jmwZ2 za1NGHx4rudKh8k5hBR;kMf|K8kQfl!N3j(8Z?2#PEcyT^uXSx%Q> z^;d!(?1D>5kgSN_ca)l$Eq@f0N|cXp-lCrXDmHiqt8ygENF9n8tDF)(UN8uUosDC zWl#{>$b4C)*VJr$`8fZhNBm_t{YRrGkZWoq8qyYgG;0KR^6j3`A z&~tf^N*(M0IRziy?QdCjR-OG%gi()<1a8XbnxyCR{I9@r*_v&jLtLP&7r0u7&oc~L zRnBgAuk*xP1A@(J* z)pwZWOE^69NAHZG!>rDIa0-Y3ckHmOc8(CPUXK-K-8}LkNKdfhKpPfu!r4itf`({m zZYPN0qDjKjvdYc+`Fw4RP9m`j}#8^LBKtWG+GacHPm8`q|pH zT9F=$!_PAcJ?%I9Kqse*J&*GzXW+)P?EB`m_20W*?o<~0eylK&LggfZTt^68E54eR zl0x#R;ONo%HBA?f0cR0C-@5|D4i4m&euhpm6z#Y<2^4Z3({)ckjS|*w|B$6^XkPEu zdO0{r(w5|3$BI!hfz%y>qjQ8J?X=1U__-jj`y^iW1$i=FOWn-HWWH`anzLNXV_8}g zSeE^i1JxsDJb$be>WQ_H&Vc+Ow~c@H_U06;MTvfh(k=y`>X{Q{8jSG%$xgG?Rfqk; z!BB!#QpIu>CY&ChU~r5b$uicSp8=`qJwLo}?Ur*?NV4x>s5#9sPi(raw;JbKe8 zcx(*C>DLI{(yP1mr?Wxg%OcEA19u@nZjZ37uYCxTF-(nH1 z$yH)%R=yJ*^yF?OLlQTc>3yI(ll!AkqSvfz>NgxqL*XGVYum?&=xYA@4SD=qTfHEX z457AmGV||Za$|Odh9qFe@aN2iXu;E2a~7?Qs3?;A%I}bMxv2VL&wvxiAp4%cr$>fJ zJ|Bae=-8mK@VDVKvOJnqA2bJ-VS&tdR+VQYUlirwXxEYpo8hJk|QY>Yn)xV2jJ z0Qk3fgu;&K3!k^*wFQ>HNCrWPTCxQ$!)Ag^A;NoA=GCK|p*+gRi7M98MR4JfVG)-F zs8ltOHEW@jtQoPpP3T1hL}2)fudb1DjfNOb3-uZh(2!pCjVjxda%3CzmCDoqoqiil z693BAtG6e@W%r17=i@rUVRmUS6H&2pDgU|a4{;1l+CAlAW|C(@I!kl>C=fnmP6@b_L*C2Hw z@7AO_l-~9JQNeR`z4wZ&XW^2WZH16Qdr$q|FPSyJ{LZPhg!kA!R@c*o)*X{ibI4e5 zTlkHVcWo3iLBr}*&6aY`kZNPaYN#l}@X^rR*``{f&~y6#aR!<`P}y%Q)$g}FVAe;^ zhFK`mg$)ve^_srHCy|!kpnHTDw!Wb$sj_IxN)D@wh^JYdQs4TKt^jXQS zALYRBPUcRsz<)A}8G^tGvu33oY|b)lbPbu(U?2Idk~dY37LKTF0M``o0@+`IAGd1? z5L6fmavNZ@7WW7bZi!d9l@5ce6t$L1E|U*+y8u~(aUqq*zGql}9p&&@oKlEj_Lb>= zJfAOgT&z5Ar5tRIqLdcrWVN>lFYSmd3m0_#QkM2&OnOit+gMQq*7mZ&H0tdAS3&7@ zGT3uZfd#aoimQEVk;Jjqi?nJehtLjm7;%mSKXdZo>>C_K1|QQV2`0I$#!D`~oKTOa zM*7geLoXYj|8PFrj#gBhx%n9Tca}ni?DY?Si-5%WI2S#^B|w5R_q1PKm8aXRjxR>9%!u?BFh?GGvWMGjGs$?~H31)n@9D4xYe(=dT~x7=k-liwR-qu@wOwc{rEL z$i>1v@Nny09H8?A#3J|FT8zxaKQ54Hfb>NfMf1^{msReET-tuAUNk+!f+J>#nh|qR_4#$R=HC( zV8w>vq9+V@Lf*w-b~Z>d6V#}F(<@sK=+9Z7%e!pTO?o^)-ZAxX;z04mo^qJ97KPRF zMos&T$P)2DuJ32;RRA1Vmb8FI7vGDDu8#o&26V7|fs&ov7r`q7btAP%&LUuj)K3u5 zMMlQ7owUz6lH3n|;oo+>pih47Puss+yQc#qH259z8++>cdU`P1TRxg^4c}P81Xts} zx>$^xWX(=(N+5{*;{{vua#G%kp^xdfS(a`U)v8{FYJZr>LYEv@Q4q{pWN%aEcqv(j zdS*A_dAY-UQ`TQF;-)G3+`IE{Xro7go2V)VWRA=?9YCb4=1;W^Wxm1t8RMe;o%77h z&rFgv@=hyU0RwsG_Gy~tF@9i*>^B98H^w)j7q{`m|DQid+cf)V3sQAefS~XrB^KHi z!dT;hNcU}eRBmE$^2Sm!of$wC}ukH9IQdOB>+)z`o{ZT~ILt4DmSSn2HVm)Xs_c&S8 zvzb&O26=)*6Qavbz(9g(URPw zdlf)X999Ul@eEhF=iP<1MH_B0-)Ci5x9IG)s8%;L23$v2#2vqdTf$f^;GrrOR@JIY!=YVwb zS(AeB&TFxK&bsEeF7LCwc?_Kg(CCvmRLKsE8}}fM!cXT?OV(7^{H?6vk0m_VntS{T z9+iKH7k60IXn3wbN_=mA&z96+UAdx~%yC31+ChX!q$4b7faX{_gJcPQU5~lH!*_LY;fsW$S>gxfkX^% zNld|a3}UDHi{`dF7j5l}xZ=;)f@1gD4_kIYZq$hApe?ZH>bXgh0FU~W^x7|7%0O#d z5JzL?`f@6zyI<`sjXG&2NBCzAJP*Xdp+Rc-o%Pm{M@{JLP`V)Z_5iAHsM`(VEn;;@nwc{#x+;bWfsWdt3S zoZ_ll?RNWIM48|599BVM8=qmeQ(EkvwP}=el%uq_6yx|%{upja%Pq++g%CQ;Uce*k zo=eZX4VRaZ`WUpYj1#65e;wn>No|QB(v7&fl#?CzF<^!SS^VWQZ{mAA^750Sk+(H- zDi}gr{0^?}k~}ZTw)}==!w9zcTq~t9V6Yp{poAYk<}SbQy-uD`Mmhhy6+^Lw*HVQ6 zV=$WXmB;vD_~^oAeHyplY0#a5_;aT~6GM+s*GRyr`TMiWZnf{b9WT5{j>W7r_=jF& zarBpWGOoE$L3|6o9@Cs+^TBP+vF`)6s#q9&DzUw_rLL1H_NqhvHMhx1)Km8s5v?~SFW%$n zr47mKr{nV)wOx-|(h$*x9e=8)ik6qxFf`GJG)xjir1&yRWiQ5@tS&;jSqp*xAj+~( zIqO$r(4B&MJ?!T)J3TN#(=;>5vRL z{N)6EwjHq)h5LSUR;h1jf`xXzg14x;tFot@t)|OUyhf$PFNALT@%K-Le;e1o&$vIu zZJ`;bE*us*ow?;kLi6pp)kOCGsa`GWWJy)EOTqtsc20r0Cp^L+Yjjb$&W`;xDnUop zG+2Rb!TwPfvBFVbTF#N3XuKkb5B_AZwX-J98Pz~xpqw>qC7N*bQM>NW^`5P0kWi9dFsuhmohe4%sN9HvA;f@&GJiJ$t{bK?fiW##i zjWWtcGtX2+n*^W(Va*R52&+2Ymc-9Q={PcH6#wd zcILeRk zxFODgUBGDw7-^!A%hm?eD4@TpPz}qg|DDJ6sm7o9YQxCZ*yMj>U1y@LA{-%)oxiQ$ zqfR?{#Nk2gR>?Cc#yHG%^FBKAzdbp`DsQ?AC{&OL#cfRs9aRV&%U4vc}bh?QiyJhAEgnfWMK*T7mv{V1aqJ?@h~kA!9SazZjBpIY7MvO>Mf;uQm~^GbjzneO?B&hT$lvXr zBS&cBV*b)f&MCh<;DH{Lh5c_BqXMNcI1~Q{DcO*A+ko#b1yUy$0cuIxEbbB>&hgYh z&K)>$moeGrl7h-h%U=9n%a8o`;RYR&x|>0}Nc!5_!1I6dGAd68%CQgsc}uo$iaFJ4 zB=7*B!*Xb%V^4ek$rr`px)%AxDgF(jIBAcx9&o1q1s+#`AukhvwLQEmud4#^x!Y?% zU@634r8_#hO6&%TLT)_H76J%kwd`IHpO&;@=KIb8Ayp6*3P7^b9*$V;4;r@pf@^q? zkQ4@lSRUA64$n_P#zS35dRo#*;yAX8$t9ZC9h34TJuZ zx40Qp77@(>UOx2*!*|YeO>D7Tls?JkDJ`o2R2ZT!3W-3o$_BIC?e>qQss~Lw-Qh6K z0SH$AjHSKjBUHM@vqEfJAtVU0LMV++^%NnRRt-rfGfs1~jPzbJN$%la8?b{F{sQ6< zX<}P0F1@y%^`M9idk`8*!~uMp$tQ+EX1mcxvy4hX{eHm$?_NllDc%dO(-|lBdjllT zrOQXxIqc>k(WljVLJ3d=V(_Z|mC2&9o*FgU>JkJ*aBqB3+x5XIIk%k?W0hvV3LEGC zBy-gzew*U(`vu~H{z+434{NHHy9Z~&(roTSY2{2epi;&CBY4w4nAq;fU0E}6MXf3w{v#f z-Cegi**Lh;IjPY@<&RK)mr4hi343NlEbrAY8j~yWT#p78`RtUf zY)W}w;?OB;AP{a%^LZje-2pEIcV>^(fNtNN^FT~=w|ocXzK#Xgoz+;6SUtm&$@M0vJBZg@mtv-V zb~Dn?EoHK|rQbi=F(r{0OQW$^RFEg9X z>>>zZ!X_D-NR=l>lF(}W1h)#xko7HIlZAG`;Yb(A^SDdO_>i@?AB@~1yHY$1P=b)zHJZR`YM;-G408RPkY)4}q>Bt35Dkjwt%A z4}MNoVX4UjT(7AVea-N9J5^>EIOCKr8{$+e-9HgpiGQNsA!D%ut4qaSFQiYrCWfBq zu^-d#IB+k-{(mtbbW8K5QT41*ZxxJw2JaM$!v4*0^E{enZ5XX+yUlE_8ahW9;vCf;o;e$Q0SIk=RL@SEvcuPV z=!ZP)&rI-)OcEP!uPaIK_XNf#J(|h6h+{w8jz$XwSBCwjk2Zack}mhhsTh2@5e3AV z4gMEXYmWfH))2#4WQj#L`XY~^f)C=&AR>vVbebHuJ-Kx8=s1VAw$oKga*$LjekB3C zZC5S1qekT$vO$KeH4EVA=r6|(gTi`3g>N?c7eV{PW;`a^5}5+fiX07 za?1^3#}+oH-*{qbp$ubssy1pUDb$O` z!R|Cn@VO?hWEvSo{qVi;10jkk{c(!d`=k97UQzGfXJQ!lC4cHD-2F+P8X#1H87{4B zxnhV>m^x~m&MHbn%fiv^@vL|Mex08yd4?AIshG-g$6jrcUGamA%)OrEx=tRyxVu!K zApGjy8Si5fwb5@JUxI86uPTsHu=%oCiS@sV(YztSMPlGxM37ehkX%#T?Mcdf! zd3$7LG-e`-{&V4{w@9>yD{(j$cn}DJ@2WVcnhadW9R!94hZO&*nE7$^~X(Vh=R~V`ZZ)t z`VIv1;PVFgcKV<#g9r)I44sN04R49jL8voccg1o;cZF7=pP(YI+ma3v^Vbes0TD<< zDQ9i;s$AfA|52bta98pc9RiIv80jt1F=s3&?7AxvGlt$M1&0v=ioHsa&Tzt=pV3M} z@1XWZme$^GQAa>0tH{k4T$!@ zYLr6B!*tBBr|bym?ubfj&;FLHRRxG&E#P01M$aMd8Y6i6sBQbSGtv6C{ zyZ**ozy5eM|D6`-W}MMrMXuA*?^-xS8hv>gs13)#AGVlNrUujq=mlujrR@aKiBOf5 zF+6(Fh1OxHoSgRp6_(5MQcz_6+6olJ`KtG)X?ti0=fvg0k&;?f>94FpV_oSoq&SQV z3X}gti=(H9=`k$NUpNibmz9z|$)GEr)$II=R`T&90VR7FKUA+K=hMeGRdC8GEyz+0 zM+!;`V-yB}MR4*Crh!7(+H^(P_7S9Ub^AWv;JflhpybeCBm*N$R~#ha378nqm67cm z0p;-1!ohPS#9$=r4|wibYIGmZki|s9xpCa;0AWLqPIt{#ktS4@x1tssmp?`Ys@SQ} zdEhOd&*fDq+=f#lRUR zkl*bF7K9}$MG{%>VCi+q|AU%urb6_zG7HCi*KpXv6?V0=hJ);Vp>8t#dbP@9s(-@Af|Bajwi8 zHt35(EL^6BBRzaVXdIo^8+FjA42XVmzBdNtlSKAg&Pw3IQwQ<5j7T{9Bh|VzFd`~G z7U^oz>2Yms?7L-TE3>=X=O)Je3)@>WHGeb%yJb*Pn{G$itu0D2y;!;S?7VNfLopV=_ zIJ$%*L#BjA-=+-UEyzkDe;ca!;SFVHaE)~~4rg;1n^1e~Q!l5#xxIjWSF8ViR~bn! zy73~~VKsF6LMqcx@FS8s3(8%rJn$$`N`Zzqe0lm3;9Zq4rUg*okatEpjH~x-6?{YG ze9)urAi9{v3e^7Fja-6SlM+aw6L9qrX91LDF28gwd#X^sJhE4oa$L|HNN7>vjfpW9 zxZtP${%`vXgjzNdg7QwlkKscqzILw^v)WTok8ZhN041OCy$neZ>Oe^zA&5%H=4<3q zQ(QOm%3Eqcjw=db`QFCGR&lX!BEH>LR-vbdrVTg-|9-V6B+ilo6gCoon%g%kry76* z2Qtn$VLQ?bGt_-3j%?;7uHFG(|A^*@DC-w8f9{JVFz@{`PUBIf&gA}vmg`0qz8B}z z;5S!L2Jm)t58?qVehx`TbM!#*^W!Fi-7d@Z9I7VI_vg?zM9y;b-cBxRgVHGc+q*W_ znaN8n}lbYwk?KrcIf$F<490X-6-w8A+AGyRt3izd6} zQFNGk3I~F*{9Ge5RDWsw(idO>b!T|bmWL^?$UKgQRwhX@9){?vd! zL_E|(X}^~~#J{USNnkT5!7_w(O4ZfRNae3sFk^rgZAsUWv_`fPFH zH%diI|3_VC9T3&G_IrjQl}2G`5TudrM!G`~>5y&(X&4&m5b17d=^i?jZUiKxq`U7P zf1dN6_uPB=Z}zOc*Rx`;S+|z{Y80Y&l@XZ7ms&8t{6%2=E^(i0oZ7 zdA9!yCP8WE{#o{Rcf3O^%mdh%trV+x@2DEU4#Lt`Y86?_=A>n(wp}+qRQ$BY;Zbb+}Rw4>EFF#n03p&?N(80)A1FQ&()Uenu%s&qX`Afalhu zHZPqJ$2y{PTa%@wUJByh>x}sMR)^Z`eSuMC23NzuFKU!VwvUbP$*CIG+ zwUmQ>MT!!)L>a5TM+3))(-yI38r|Fl8$=gm+qnkPav?Xz6L-!}OqX`DqFAtQB68cc z1t3S#xCNX-kDrkO``*q+hd{Ka{&|G=#&M^5N8U(i$Bl99+}nhZhVI@}wI`UUm|+eW ztN{K2hGoJgw>j92Zd?x>*rjcYMI_goBK&%7(J_;%qaGVV< zDy07aoSuj9o`qOmTCytJtY(E|OfAw=S>$S6oLHMh+IMBXioK9ooi&b7vJ~2CIMMoY zpEz3ul~!LFI`y73`I$8u#sywQdU!iwYOL7rH3l8DJXYPT784Kec4{{H)(DMuxE2s9 z8SY@7)dv*Wp>Z>&SEqy%+4eNUoN?BIpZ~zsJOoZmTdT23p$!I=;z0JTsM3U8`jOX2YIWyR01BAQGF@4*7dO=lzwspnOS>k}TlSlFdkgH*{Xe|bYmShEdrFBCmu_ItfCHWT!!#UfE|7&`$yjMyZJ?G^cuOFtoJR~jkT4lonkUm zS8XjjAF%sqfQ2L9g&o?aJ9eU*v5J-#OD9>$y-~jLv?AViZ$J%S%lz?ggZ!-IMWyy< zQiLI~Z<1czR7(ld!hB}BXzuMf*3)R=_&g2aCO(vHJoKKS1@y6g_H_GbBL%{WJmEp$ z5Qj)Qv$q4+lS2Jjg%kxC1^&$EZzO3(hNVkg=OSxRWCy6a(6?mrl#*ES{19B{8J(an zq)oD40v5y}X=L$Gn_pt61>f-&mKD-{$!1?%I`3Wl+cZpH(Oc1L&Q^PB9|2q?1=(j5 zpY7bD)|`*&Egv=gdZr*}i1>TPCYAXEwa;mnbL!oGnND-wrsrb(la;O4ArD`N`E*Tf z_3^9DEJ$vw#7Oi<#rl?^toN6_mG(nGg3t!fs2z|~!C0t=^LBd7WUcGBI5VLy>liEY z_Clz?K=s)#z>?ewj{&8=S(h1o)StVhsoo1Yxh#nklZgkPz{Hf)rO)$NLFZ5B%rXtF z^LFc3oaL|;bi#RA5f#@oUlX($*D<`3CgWlJWrX&4H!^)IuiHlKKk8LGyg+hFncNTh zt`M;hn8{;a;Jwyf6e>b#O zC<@x@b{#xcSIxG!-<8VkY<S0L7D=wmR2vHy!s^UL6VR~^{00_o3u z7>*6DG;C!7yInKP%8e=D(kpB`4sUspD8mY#WZ8*cxoqI~Q}tBkXCWwBV5g|#z<_Nh zB3j+8TxV&C9c&xX(_yj+2*v#~>q7k?$DQ{4lf!|M;x@cJbXa zRszvYqtH-M+SuTMn=8)&eXx0EY0pu5*1tP+EwVf5d8M7^J&|5Hig37W`sEJ&Aj5bf zkRiEY$qWcaW%_)HGI+1=QKhEnH#VmJMqH*{p!X>OHVXb{f{ShJ?NAGpc;1t>Zko=8 zWhIx1|7f;n&1Lc3xYA{O`l_qnUF-TIxt+Pa8#4CmFNurJ&xVP$g%;XOrY~)uE4o~E zk9T_hOgtB(FfD5AR|l#;iM#S{F1AA_C+2GYu*idd0on{Lt}h5mrfu{pSE^R5FV=Cokof-j>SgnVwzv7uA%- z#%4snAD5%Uy#4n`qkY~dh>i*pB@NEcgBU2Hnmd*nK);*Y3Vs&+=k*IPEXk=lBxuGZ zNAb0#$-(QQBo}eeJ_Z89bH!{HBi!+fh#hn_a?`$X2$-q%)Y06|y<3x^S3*sx`H|_6 zm8u+Qtw1*}!_R(ZYcgPH2fwE1ItFil?=FfOe|Mn)`@tHZ;*< za%6ha&J};cFQ54)28E3JdjN18`^T6(gzEdX_D>SvmkzTbRVtNNYuoC(A4>DDwt1Bi z!!Vh!pK8Pq*c%jPbwreSCFfz4AL@Q$_FA~GU)Uds?le5jA=JEmKk@NYO_@~HI33SO zIIei33Csh@=dcd9F&^Hp)r~!_hjGfh2}pQbt$^WsLL$>SVHVgoV(18p>cW zS{xxYac{mrDE0N!SV*PY?baiRUEUy;c<4s&OpXbk%EBGH$2r|E)%%DB%hH9mrU^;I z4arSr5kuHXXLZO*9IxmXF7=yNcdeHrjOtiIHhFS=Y@fL&Jc~~XZhT~b`Q?O^blXEz z#Fx(6-SE%hme3;?i{Np}OO@38tXS(5f6jMpsKZ=Lxj)?rRvLoi1xRKOuY&3{ir?*z zsAPN0w+SvtW^PFKQTt?6p-bl$7wd;!;I*&3lKFlCg;!AoQ~|l2)ag2I1^CqHhY*Lo{ zYHF;o67nfCf9^wC_VT07qAyBM-t9+oRl;aX51zPKZBfV5(+Mzr!lst@yoWw;?u8qT zRoMu>tJpV4@}ad8@Qf&sORP{l(z`kMRX4UNI4;u~!30ZdwL7XRkGz+NP=FsDQNvDx z4N)i0QQ2SE%&p7=5!gp}W(ij5?24l|GQGrmj z^`oes9~HCNVi-_889@&xptbu3&UlTGdPZ^oMOuJVM3SLB@o~kKhoXYZXWZbCcpt z1y~}s$7p~vO+Mh(<8Hk}$_FHq#8KY?90sS$nIGB(*Ish#re^$K+Dpg~tx|2Z#s=V? zs!+JB?$h*PV;ayjJzRL~5<54-2lx~WSE@?*qpS2kB=P-j5-!|;*sZ3se$KB5U4KwY z1pGm0S$=AS&1|0$L{GD^%ih9~t2WXotSDR`q5$>Ftv4wE8W-wbuv-=H62Sti1a^B` z9B>WVVd^zyVUkufHknsD3UUC#_(fKO^(|b0?-6kA5K}T8p9QGVKzyyq>!3aopo*6| z<0vGysslAZwd_M#c9wd7p(0!g?!Td8-#aS-9RR|V27HcuKP!!o53s-tJqkPcy(LDD zQ=2IcZ%q&}w8N~z;{f~KfcJKW=J|Uhk!Q651jxt>w~$$88ps0rH%ZhtR0X(Tm7e(j zlOlox>ks}iM6{}Z2qMd97T^=t{w0WFq3~E)J6$+ibOOjB>fT3>nn^`{s4%wYY}a6q zLu3Si%ubP0{Og!EuI;ggf~4bGM|I`2(f7~aT)MngZ~k(_A_IF!6kf#-t+l2dJ&BJ-RTS;zzbOVZ-aG=oBV<$_1`En zGl9&j(j;J4h=t8~KVs?FpD!)a z)DC44hd)xX1L>pyM(5aJcmps$tVLjI{f{2npTcPD!}K;C>B!nQK0u#@8cWX+F1~?s z-@zFl2InA`GaYDp`VXtiC`Ww{y!=X{@DVt3P7mIvfz98*Wtb-?TvQzE5Ox>g7%(UK z#l+|w&>isB10NP*zanez!fB%? zKP9vOH==vG0@pO?8BAabr;Zl-`X>%#LPfN79Z)P{1n^v?N&SxxOgytj0rEmrfwD(| zbVAv)K+LxS4{(wb=8F%U=$}!(D6N~fPGDnzpGd@3Ec8l~*u8=WuG41#GG+uerB%{B z?T@_+xJTeW%I^_x8~TLH`A$b)hQl*>699SZxgI!ND{VcWPjCj5xW57cf*660fZXLb z9ua*2&4+XoP6J5^@(oYWQhF8uy-TM1T#1FvlhZsp;bJ`6M3ISlp~xIi218`QYUKB9*h3Io0BPpc^>`KX&>e=t zH1*#A{i^L8bpGy#O3p{q8AAF__IX+r00FT(_kalhrw?)*-)tUJePKhkeRcxmYqqT# zhD4j%0?LR9jxKEKb$oEybEf+>2~W$3UIr%%^ReU6NvD1}h2d<31K^AToNbcMyV(Qx zfkGtR07WVw_1P+Hg{y)kuxM4L8lLCObdOt5Y&RLfN7X{$`$sx6n$nGbl~?W^viAC) z!%4KtOdazk+Ur7??9l2SfWDskCKaG5|LFq=VKRiOXiRjq&>Yv6wTM4B%b-$YqCH+3 zagh}sy%C5|E%H<5tD;OF8v~9_xa%LKI^&dQeDG%N7&Fcy7278db=jr`nPHx>N4pQN z9Kg?9@Rqu+D}wUNc1-wx(BZB4z^N83(&ZSC&KrYp0tFuCM#VPnpBp7$eRu=tfQx)G z4=dm%BA*1Jcd8XrxveldFB8>xCzgwse$&s;9-MnFC}0Bo?1oav{o_4~*+&p$ySn$g zFk6ob`5V&_ZV6nIjKf^*`IxN(W2>RgaV_>eTivb7jv7<=nmh~<(aPMuGs?U=9vclyiQnWz9kbKIz~@| zZAOOWV+i)Bt!}W?^~npw;ae;!nB%-vAtn+K4fUb;($(wvbAnw)vn?4(0z6+jn|eh@&YO~(=qO12v&mcfLiE#fx;i7l zY$e8jj7+0#W#(fvkkrv}PzfBRVf3tJpZxR#i$Rd7OMw`r&KS0n2g`o;!P*-FBaa;R zOnA0{JGSDKr>LaPgt|$r*lj7548m)DmnnIOQMb*{me@iHaZw;uysVq^_9vOaXhQ}e z{t#+TkBhD3_T4lfS15lSgQpbnt)K3aO|Q!bm4Yro>+}RQWmFtCa=dr&F-m>{ZD|At z!V^|ER(bcG*$>H0Vl*`@ms{Eb4bz|N`@CGDi*|uct0k4aQ)Fn!=hi*~EOCWaSmVsT z29$y6`0ahDeVX~p+dJ+#Q?d%GZlN`jlS#CROrs5%N*|~rD&n|DvI2FU5fN1?K3HAJ}p?PB5m=0D;)V`U1J?oY5EhL z3V`fvv1&WvY!`@(w728b`L+1>r{YFBbDcbIWXCzrjWw{}?AP8rF*4vWw66jiA2zc( ztoE=;3^VyW2^p34J*3li7eA)nqAy2TeQUg$O<*9K5~19PwcFw1&XyuMkraq9^D$pM zq_=ZCY8q4ZcFB6M@kK<()^aB?-U_lkPQ=yD^(8VnJFe zIhxTzeE#lCh|r(@_~_shF=?uYE#v-dJS)Db`AS)fK`O6mSPsY<5M#QdQqhtrTnn+pcmsfb-sB14WEBdgD}JOP3$h;IkVmFH>Dh_Qx=#~?uV{T!MYKj50vwxd55}9Vu&r0Y?v?2 zp1ngQa~>U?tmt^fXm}jzMD-eJ!H77}YxMmOAXO?@dV5F?+CfrV^;sE=Z%9u?KqiVw zELuKFL%AX|dh|qOiTh-TXv4$uAXLT2a^hBC`yKAej?{x00yEtt}{#EM69 zBU(Ym+L<=)ccORMj~aU&3wP$}5sv@-Bb ztkVSAbV&8{NKnd!k?z=YV?VM@j=D2Js|CjDIrNNYKK2 zWFm5X%h8Ti-Hc7hY6qZJ>WF$4hqy_G8nFMUB)b2*5n<7K)LVraM z_jKFQU-LBD4Mg%9I#3^ElXA>aiB*4?i&ptFsKx$VEY!X4;7#pVd1ROdwRW`kUjMXT z>+IBC*~Rn(?Yz>Z)eJzzd=a(u>GH9b=RiQ0sq)hz#+;0j2py6hSvuj4L1IKZLT*d+%#YY|215zduQ=R_naR?FpA+DIwj3T$0>nv9`B#Ct z7V-Ix%PL*<>X1KI)B%Hs74i3wkSj%{3ii8I#W2A02b2gqD%MO8rcPtraG1hFR=UGb zSpCuTPk|u4q&?*^(FEGOz!w+urwuHm(i%z=1|pd_$Vnj+w~ztebYkhnMNa|~gQL~e z8JAt@N?cz9E`RIK!^N!6ukah@Kc}{Eb4M`3MA8fDY8lFC+XYgPAYL*-HL2zzbBH_j zrhZ5bGTJt0(@nptq%3#Nm(|otUwe$9V8okfXMJs$}pd zW<3%&xg0)^t*pbG>*75D5Q=020}I&CA?29B1UEFqXx$c-tHt=~A$eaei}0lsZd43P zwSl*`cz~hjdBm|W0K;XX5MBZZsq6&}OaTd-Ok*@+HYtTC_u2i3C8pX-r1v717^MOG zKxFj>+I+ZrIvNL=qsVH|t<_t4Zs*jB=_t-Oc~)X#wL zKB6Ug^_M}CcxCIiBQ#+^__>|;>FQS{d?zIR3#SkPAau3i{LU~i^aH5s_j?MqO3~!) zMZT|TbqL^Uhhc(wUMR!k!9and05B^|Nw=O>3BIXdf;$?4eIx2z-`EsW{v7rK7e12> z|6<<>O2g%@PN!|-ABu|q8-gwoY8GM`Z|;%Ga17~%Me8#XwAtNE=K=dYAmS_4-T-*k z7QS@r5(kOrr>0moYD3nx?&8Pwul@yU02$Q7Khz{t4p*ffF`@#3OWl9}ge`a9HZ&gz z9ytb=Ho`HP^EY!{;Guybl!TjI=Ou;y!zA=98!P7pQlHw`);Lt1Ym6{V|2QfzX%${l zEp0#W41bF1mE$&?ZOF_cbnP2R|I>Y?aph`-|EK%U#y4x|H=jn-yy_&|xAdPjUb?#6 z2`_8gHXKKp#OqfuWVD=Y_e=|tYlm@A8`W8{@7z7?rGHF(+SA(ifNI{4fzgFkjqA^E z{mN?$;>62%{AAG)C->tRjfi(|T4Ikdair5X4gAEU%f(#jHS*V7FFvwA3VZx06#5nI zYrtgla>l9R4?~ybg(>~kMbCDZ@0*pUT0iRB&dORDS&CDiAIfiM8c(Kyuu0A~;-G~k z?4IeHAL}iq7kaT}Wc55emXF7$k{r@D^Q-G(uXrq`I3^k8;&ZHP_c0pmF15LLZ0L%= zTVWH4B2rleIP<>S#Ge{99wTtEdRwF!M<+FE&4{wNxM(XxZ#OS^p=x^3D(`afol}I1 z>dnN&O#!js1_!JDV9orNYDu2#dac-Xop4ri7_C^#4W55@C@Rm*;1$83s>K@T z9QIq6fyPy2kQSSMf{^+L*4LNh-Ek|NDewspdLc<77w?c105Qb9V;BjxqV=A(ShKbpbnYFB>ZV{88CYqx4)riw%y ze>g-(h0N0vPXdx|tCmydN0{l_C>S~4(a9Spn5Q^zyw{$iTXA`qw@%b68bFo95NhRR zDR^`ub#IWXF!=@twR1mQl^>Y|atTno{^qWUl_J3-a@1yVy9@*?XkK3R4&g{#Lq+DB z+pH|EM$lNJHLQk?P%f%G zj?qZ2{+etp^eWUQIgXt-tHaQR4@p*%pKH% zGR6B#?$VOzRAXX>whN&zx|sDRcyg*wnXMCI8k{LaUvt+-@p!ReDPy=zeqxH0#gq6A zXH9Ir9J>d`xYd04ZF8B; zGJw(0DCMH;J0A+6q9NDk%wR~~VCxdX=d=xUb+{4&?h_~w)jaNSvk|+Y;V{B#dDbm# zh0(tXW0F{mcNWun2HsEmj>k<^zFmTxN}Cc`PBcP1LKR2@W`T_p+J)M|UV0P|$5I70 z7+-dGVcfNU(f|MWY?aEby4KvEkOH}ykVgLkHRF=IN*-pdOQqF7te!Avk%yGj=2}Pi1IU7$Y-6f*Jg)#ZY3rucyW6`^|G2@73>* z>v&cQI$mnJ9_UIVp;6g)>}bDSn8(zJRJ_?57X}7$9Hqn*)B5W&*}yzBL^;h~C=DkL z3nFF)v5}PCcS~~;m5>!MGWvQtWUWndm;|=xAXh|_L)}ucW@7{<-yE(?$p>J0$pPl9 zsZbJdt8n$NL}xSx9_EZHm_^MFFPs`K>c`>sP?dt9gd32M?{d_XdNTvCBgzJ0QTyM`o}~_WLWJ#;dkAqxktCZlhxdOW0KG z%+sek#Z_XMmyy^!7Z1PCDq3G>(lP^GW{h!ZQ}}|*hcw9{Hicn)kgd)2?A_VQ@^rCO zrmdz8ukCBydWM6MtMK}wh5IHgvQtqCqM5DPl<~}8UQ50r`>fHs7biD4j>{Iyvsofn zwyE=9HH$~4y|b5eYUXZeAC9`Vkn_k?-W2(`j4)Q5How~=Txu>~uF?0dF)8slojTK8 zqc|91^S}2_^JqTau{kG7$yzw(}rUak0Vi7O5&#s-qH}Cvz zOlD~^F2AJC{i4%*otsBiDVVB)iP37Cfj-jiMSgfbQ@#84dg-!{wVuXDhdbwwO}ld= zt=r;Txl+WsZ62wGA)FNy4X$mgA#3XU7l7ct%H4_LpTpCG^Bj1X2fT%U40gQ{Pz zsH6$P|3dHnL)xw$gSD!ZqMLTBEsq_JjsPss;cqu z(?R6O`piRHtqJ8IY?*>vS(sV7sfQ`6|7Od{^Y_8-%|iy4eWs1m%iiE+P%@H_#iaik zG(|H8eb>qJ`m9Ynuqc_*z)j|sp`r?~48vVt&8e79l_B!yJ$?TUC^wRQLSm_JGfcFM zM-i)5DX;3{^QXecHAZsrpDs$IpOY144+x=i8ejSe)f2>~U{JpX_xAa?$~B@to$~S< z!dUxmfg*|YJ_uL7OMHScq93~`TS;K9<@NKQD8{BRdL*&+*URnmHuDoB&0FUQOYEXB z=k-frIIVcfy+R8$smUuSH*zmw$n>TBHor=>Wb3WA@`+18qyvu z$8A$)w0C`I%b3#o3<330|7u4ey%k3LTEzj&1DTJ`Jwv)D*@o6D@j z3t2uMLyy)Pj1$bp_`) z9j276TWAEXckVGM#p%e;WVC)uws-2H8Rm=j9?p?YdKT09L)VYd+;~!w&(=H^N0GJ! zsDUnpuP1st1GJIpUOmTO=XZNtOF6!~pQZbCWEJgcS(i%>efo_eHv!epyP=^pl)Bep zc!<1qSij(C(UpJPOjv_0$fzD~ylxJw7pcYmmbJ!0vOP^|QYBWGab$tYr;U z@d-01M+@qaeb^Tw5AJj9!uC_cLRdg${kvgdOu{{Kru9NRhk`i?a!V?#NG4v;76X4N zuG5$T5S`M69AEjV!J9xEipycQ4=hx`T4#^qwFfTQ;8l4}dh*zlD)1*`SEba!u4H_o zkbzu-_$@jYKON=j5xvs*+P#n1NV6(NecBHf+=8Cl}fSs z#>KZ>K&z{uZ?BIR_Xi|jNYGceP$T50qm8elDE58oT6!U6ru8!rWPk1A@14MnDZp1iGU$SE6B@1SJ^bu9$Jb9UdlE(Fe4q0ij-_R_B z1Qi%tN*1HsB^0R*BX;w z*kFYb{`&G~qASV;X!-tAaWva$!iXiT18K@Dc4Q|03K9OhZwf;Y#Uj|82H&^7gnfDG zzQWig_CAp!r!*>0<>ey|Bkzl%_YP%CZ-X1%e9++4n$CZsP#uK7V0%M}?$o>;(cMk0 zFV;SMy8{E$=$;mst)ni`#yNz$i);3UhPh|lM8PfPMGfN`X>IR{2-ca+)2 zx)T|fxak{tQ6!?nH65k$`MXMlFp*Iv*|ZluQ3+Y6{0Z7f;s&f8lhKtojS;CLE=W!h z92TS(hu}xj|Cco%)~LZJP__3{(br7M7 zA=%4V8pGb$A4igLR*paF4C7vyEJxl>a!oMKN*>0Bd1*_M# zQGq35K&EsA-kji2O=7}=p{#?~ORTbKsbk7%*PFvn^ovd0{tU}8Ef|5r_W5_+t-NHL11I=5eynL zUTwv^e(TE*BGb(aaiWd#-IXNPWX&gwm9_1x}wJS?)fBE6sgbwY#oYyVGeiTi6lgQiWSOj=i zvQLT#G5$CUF2!^>pL-7lp9v2v;|j!+IBXe7)-GbA>&i>y(z9)O_rL#Tf4aJkO6<7y z$Jj-ub&cKtVoc$#P<+Q$Usf*sq2kvZuqQ7X?98UrMn(GBPSu++*!Yt$3;3_7WYerZ zk|{MPXoc1Z;Ho-`y*JUuH_>tGq9vSdD%TM+rne2D23{3}zn%+VOeU~F0EN2-=Ch~M z-+)YKUi^-b1k=Lc&Xe2XBr4nmD(A$&sFYwJPZr=Y7h`#f5@v_WF4b$;C-4OX#-M2e zT%QW(=m>DW^W^NIujP0jzdI2dEbH-;Cu|5^z7~@@I5NzbA6Xz9+yYj;n8{l%*gn5l zx}j~(-Iz+NNtt)uqSGV`3v$57g8%wRQ^5az@*jur*l25gY3*e0I-kM<*S?Kd5L3WA z4ZO)V2Yw{3Z=JlsfqXWjujNG(qL7W__dn{|))3D@`$!$*k0>Q%dBl8ypJCOKtE>wa ziaZuIdE&=^{489g$0lpTNsP!1sk~};C=q_+@_B-B6dHLC507? zFv9vb55l&sR=U-{z2-I43jlc^3yt&5T5^8{VO!g43UhOGWX5MY)){|K-hI{7mjn>@cCxil3{KpOvUF79|Mbh@pe1AOaYP87#5l;q#+t~=vI?M8 zRC9PTC0ZyLqrEH?aQA;fEpdxhp|+JJwNzLfadWv-kZsPmjg5TIJ~&{ULqIIijK zLZ`Cd-uEuatIrNb$we^DY(PK&-^)pgzmh1!i^FuPwYGy9!Ep^5j>IBjy4>Cz{JMx! zE%6YvaU6?k8~neofl%FW;>ev)oCC#ya^!L~#0yaNWjSf!{{*5eTm>d%X`#zS)96G3 zPra>ahX9y`#`Lx(1hecpz~#Dy7>2)-!4`MQ-@smk#0=Bp!UjooC$IYh_ zabPkzyT)Lk{ru%MI{>a~<5F}XyGX&60~l6Ql+S=}qrCMHH_(p*j>VdEC>T0|=RfzE z0=p5KJ_^Q)VgX+qIhJW!pkP!IRYkA?gsd*rtr-f~Og^LIVhsyG;VyOxSu~SO!NF5p zW)x5Y>By4+mXnN+elZ1;=cVY%lzaz3Q!HKJCvdtLtNP0~KnLMP*3|4QLOWK@D;%>> zsh&bbvha)3O9AdyteB#s)B_a9uH(@x!My$bm170#qrZyA25aMh?f!};ra_LL&#Y_5AKvG9ic+6HpaA81{?u>G%jKFqw(3_uGHtxjB&^ecX=rWP8QBz$1Q6kbmfDn5P(X*>59{Ik z84`-17#*|Wd)}g7buXyPW&{AT%o~<9rH46NSZiuH#lObC2F6uqa#r+Zoai6{x+YGi zP*#T}5EM}Gi(rapTN~?^l3S_|SH(@KMz)c=1R41eo>z9#JA%XhaO3uNU zeFq||dUzjVjf~mV%K5<;vV8nL;}Xv)}B6bpdwAY1nE(0eFz&6r>1T*3I$?XV@R_= z@Q68nF)%F<*Y*yI0uM4>N5D)1LevI8#PFhVU@jw|2o0(w3@9Rt_4me0-|Fcq-E9Vk RIt1WPPD)v_T*Aoz{{Ys5DPRBq literal 0 HcmV?d00001 diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt new file mode 100644 index 00000000..cdb5dcbc --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt @@ -0,0 +1,226 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.R + +/** + * The mockup's account. Everything here is local, fixed data shaped like the gateway response: + * the UI can be reviewed on a device without a Spotify login, a native library, or a network. + */ +object MockSpotifyDataSource { + private fun t( + id: String, + title: String, + artist: String, + artistId: String, + albumId: String, + art: Int, + seconds: Int, + explicit: Boolean = false, + playable: Boolean = true + ) = SpotifyTrack(id, title, artist, artistId, albumId, art, seconds * 1000L, explicit, playable) + + private val nightSignals = listOf( + t("ns1", "Afterglow", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 227), + t("ns2", "Static Bloom", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 194), + t("ns3", "Midnight Radio", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 243), + t("ns4", "Paper Moon", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 211), + t("ns5", "Low Tide", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 258, explicit = true), + t("ns6", "Neon Rooms", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 202), + t("ns7", "Slow Motion", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 236), + t("ns8", "Satellite", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 188), + t("ns9", "Wide Awake", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 219), + t("ns10", "Last Light", "Aurora Vale", "aurora", "night_signals", R.drawable.cover_night_signals, 271) + ) + + private val glassHours = listOf( + t("gh1", "Glass Hours", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 245), + t("gh2", "Amber", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 201), + t("gh3", "Held Open", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 230), + t("gh4", "The Quiet Part", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 218), + t("gh5", "Half a Mile", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 256), + t("gh6", "Window Seat", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 192), + t("gh7", "Soft Landing", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 274), + t("gh8", "Glass Hours (Reprise)", "Aurora Vale", "aurora", "glass_hours", R.drawable.cover_glass_hours, 133) + ) + + private val coastline = listOf( + t("co1", "Coastline", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 232), + t("co2", "Saltwater", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 207), + t("co3", "Blue Hour", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 248), + t("co4", "North Wind", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 196), + t("co5", "Open Water", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 263), + t("co6", "Every Harbour", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 224), + t("co7", "Drift", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 181), + t("co8", "Coming Home", "Mira Sol", "mira", "coastline", R.drawable.cover_coastline, 286) + ) + + private val roomsWeLeft = listOf( + t("rw1", "Rooms We Left", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 239), + t("rw2", "Hallway Light", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 204), + t("rw3", "Furniture", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 217, explicit = true), + t("rw4", "A Door, Ajar", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 251), + t("rw5", "Everything We Kept", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 268), + t("rw6", "Keys", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 185), + t("rw7", "After the Lease", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 233), + t("rw8", "Empty House", "The Static Coast", "static", "rooms_we_left", R.drawable.cover_rooms_we_left, 297) + ) + + private val goldenHour = listOf( + t("go1", "Golden Hour", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 221), + t("go2", "Marigold", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 198), + t("go3", "Long Shadows", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 242), + t("go4", "Honey", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 209), + t("go5", "Porch Song", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 257), + t("go6", "Warm Front", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 188), + t("go7", "Before the Streetlights", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 236), + t("go8", "Still Daylight", "Ivy & Ash", "ivy", "golden_hour", R.drawable.cover_golden_hour, 265) + ) + + private val softFocus = listOf( + t("sf1", "Soft Focus", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 214), + t("sf2", "Pastel Static", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 199), + t("sf3", "Lavender", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 238), + t("sf4", "Low Resolution", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 226), + t("sf5", "Silver Lining", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 247), + t("sf6", "A Little Blur", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 183), + t("sf7", "Daylight Savings", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 252), + t("sf8", "Out of Frame", "Lumen Park", "lumen", "soft_focus", R.drawable.cover_soft_focus, 269) + ) + + val artists: List = listOf( + SpotifyArtist("aurora", "Aurora Vale", R.drawable.cover_aurora_vale, listOf("night_signals", "glass_hours")), + SpotifyArtist("mira", "Mira Sol", R.drawable.cover_mira_sol, listOf("coastline")), + SpotifyArtist("static", "The Static Coast", R.drawable.cover_static_coast, listOf("rooms_we_left")), + SpotifyArtist("ivy", "Ivy & Ash", R.drawable.cover_ivy_ash, listOf("golden_hour")), + SpotifyArtist("lumen", "Lumen Park", R.drawable.cover_lumen_park, listOf("soft_focus")) + ) + + val albums: List = listOf( + SpotifyAlbum("night_signals", "Night Signals", "Aurora Vale", "aurora", 2024, R.drawable.cover_night_signals, nightSignals), + SpotifyAlbum("glass_hours", "Glass Hours", "Aurora Vale", "aurora", 2022, R.drawable.cover_glass_hours, glassHours), + SpotifyAlbum("coastline", "Coastline", "Mira Sol", "mira", 2023, R.drawable.cover_coastline, coastline), + SpotifyAlbum("rooms_we_left", "Rooms We Left", "The Static Coast", "static", 2024, R.drawable.cover_rooms_we_left, roomsWeLeft), + SpotifyAlbum("golden_hour", "Golden Hour", "Ivy & Ash", "ivy", 2021, R.drawable.cover_golden_hour, goldenHour), + SpotifyAlbum("soft_focus", "Soft Focus", "Lumen Park", "lumen", 2025, R.drawable.cover_soft_focus, softFocus) + ) + + val playlists: List = listOf( + SpotifyPlaylist( + "late_night", + "Late Night Drive", + "You", + null, + R.drawable.cover_late_night_drive, + listOf(nightSignals[0], nightSignals[5], coastline[2], roomsWeLeft[1], softFocus[3], nightSignals[7], glassHours[5], coastline[6]) + ), + SpotifyPlaylist( + "sunday_morning", + "Sunday Morning", + "You", + null, + R.drawable.cover_sunday_morning, + listOf(goldenHour[0], goldenHour[3], glassHours[1], coastline[0], goldenHour[6], softFocus[4], glassHours[6]) + ), + SpotifyPlaylist( + "focus", + "Focus / No Words", + "You", + null, + R.drawable.cover_focus_no_words, + listOf(softFocus[0], softFocus[1], softFocus[5], glassHours[7], coastline[6], roomsWeLeft[5], nightSignals[3]) + ), + SpotifyPlaylist( + "kitchen_radio", + "Kitchen Radio", + "You", + null, + R.drawable.cover_kitchen_radio, + listOf(goldenHour[1], coastline[1], nightSignals[8], goldenHour[4], glassHours[2], coastline[4], roomsWeLeft[0]) + ) + ) + + val shows: List = listOf( + SpotifyShow( + "long_way_home", + "The Long Way Home", + "Northline Audio", + R.drawable.cover_long_way_home, + listOf( + SpotifyEpisode("lw1", "The Road Out of Town", "The Long Way Home", "long_way_home", "12 Mar 2025", R.drawable.cover_long_way_home, 2_442_000L), + SpotifyEpisode("lw2", "A Room with One Window", "The Long Way Home", "long_way_home", "19 Mar 2025", R.drawable.cover_long_way_home, 2_118_000L), + SpotifyEpisode("lw3", "The Ferry at Dusk", "The Long Way Home", "long_way_home", "26 Mar 2025", R.drawable.cover_long_way_home, 2_676_000L), + SpotifyEpisode("lw4", "Notes from the Coast Road", "The Long Way Home", "long_way_home", "2 Apr 2025", R.drawable.cover_long_way_home, 2_307_000L), + SpotifyEpisode("lw5", "The Last Bus", "The Long Way Home", "long_way_home", "9 Apr 2025", R.drawable.cover_long_way_home, 2_529_000L) + ) + ), + SpotifyShow( + "small_hours", + "Small Hours", + "Northline Audio", + R.drawable.cover_small_hours, + listOf( + SpotifyEpisode("sh1", "3:14 AM", "Small Hours", "small_hours", "4 Feb 2025", R.drawable.cover_small_hours, 1_842_000L), + SpotifyEpisode("sh2", "The Night Shift", "Small Hours", "small_hours", "11 Feb 2025", R.drawable.cover_small_hours, 1_976_000L), + SpotifyEpisode("sh3", "A Film We Nearly Made", "Small Hours", "small_hours", "18 Feb 2025", R.drawable.cover_small_hours, 2_035_000L), + SpotifyEpisode("sh4", "Room Tone", "Small Hours", "small_hours", "25 Feb 2025", R.drawable.cover_small_hours, 1_788_000L) + ) + ) + ) + + /** The default Liked Songs set, already saved in the mock account. */ + val liked: List = listOf( + nightSignals[0], nightSignals[3], coastline[0], coastline[2], roomsWeLeft[1], + goldenHour[0], goldenHour[5], softFocus[2], glassHours[3], nightSignals[7] + ) + + val recentlyPlayed: List = listOf( + nightSignals[0], coastline[2], roomsWeLeft[1], goldenHour[0], softFocus[2], + nightSignals[5], glassHours[1], coastline[6], roomsWeLeft[5] + ) + + fun album(id: String): SpotifyAlbum? = albums.firstOrNull { it.id == id } + fun playlist(id: String): SpotifyPlaylist? = playlists.firstOrNull { it.id == id } + fun show(id: String): SpotifyShow? = shows.firstOrNull { it.id == id } + fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } + fun track(id: String): SpotifyTrack? = albums.flatMap { it.tracks }.firstOrNull { it.id == id } + + fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { + SpotifyCollectionType.Album -> album(id)?.let { + SpotifyCollection(type, id, it.title, "${it.artist} · ${it.year} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.artistId) + } + + SpotifyCollectionType.Playlist -> playlist(id)?.let { + SpotifyCollection(type, id, it.name, "${it.owner} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.ownerId) + } + + SpotifyCollectionType.Show -> show(id)?.let { + SpotifyCollection(type, id, it.name, "${it.publisher} · ${it.episodes.size} episodes", it.artRes, it.episodes) + } + } + + fun topTracks(artist: SpotifyArtist, limit: Int = 10): List = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks }.take(limit) + + fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) + + fun library(filter: LibraryFilter): List = when (filter) { + LibraryFilter.LikedSongs -> liked + LibraryFilter.Playlists -> playlists + LibraryFilter.Albums -> albums + LibraryFilter.Artists -> artists + LibraryFilter.Shows -> shows + } + + fun search(query: String, scopes: Set): SpotifySearchResults { + val q = query.trim() + if (q.length < 2) return SpotifySearchResults() + fun match(vararg text: String) = text.any { it.contains(q, ignoreCase = true) } + val allTracks = albums.flatMap { it.tracks } + return SpotifySearchResults( + tracks = if (SearchScope.Tracks in scopes) allTracks.filter { match(it.title, it.artist) } else emptyList(), + artists = if (SearchScope.Artists in scopes) artists.filter { match(it.name) } else emptyList(), + albums = if (SearchScope.Albums in scopes) albums.filter { match(it.title, it.artist) } else emptyList(), + playlists = if (SearchScope.Playlists in scopes) playlists.filter { match(it.name, it.owner) } else emptyList(), + shows = if (SearchScope.Shows in scopes) shows.filter { match(it.name, it.publisher) } else emptyList() + ) + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt new file mode 100644 index 00000000..f9aff9b6 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -0,0 +1,402 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.Image +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.focusable +import androidx.compose.foundation.gestures.detectHorizontalDragGestures +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.indication +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.input.key.type +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.ProgressBarRangeInfo +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.progressBarRangeInfo +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.setProgress +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.apps.media.formatDuration +import hrln.interactive.sidekit.design.action.ActionMotion +import hrln.interactive.sidekit.design.action.ActionTone +import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.components.SidekitIconImage +import hrln.interactive.sidekit.design.components.SidekitIconItem +import hrln.interactive.sidekit.design.components.SidekitItem +import hrln.interactive.sidekit.design.components.SidekitItemRow +import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.SidekitTile +import hrln.interactive.sidekit.design.icons.SidekitIcon +import hrln.interactive.sidekit.design.theme.SidekitTheme + +/** Square remote-style artwork, backed here by the mockup's bundled covers. */ +@Composable +fun SpotifyCover( + @DrawableRes artRes: Int, + modifier: Modifier = Modifier, + shape: androidx.compose.ui.graphics.Shape = SidekitTheme.shapes.control, + bordered: Boolean = false, + contentDescription: String? = null +) { + val colors = SidekitTheme.colors + Box( + modifier + .clip(shape) + .background(colors.surfaceA2) + .then(if (bordered) Modifier.border(1.dp, colors.light, shape) else Modifier) + ) { + Image( + painter = painterResource(artRes), + contentDescription = contentDescription, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) + } +} + +@Composable +fun SectionLabel(text: String, modifier: Modifier = Modifier) { + SidekitText( + text.uppercase(), + modifier.padding(start = 18.dp, top = 7.dp, bottom = 1.dp), + style = SidekitTheme.typography.small, + color = SidekitTheme.colors.surfaceA6 + ) +} + +@Composable +fun SpotifyMediaRow( + item: SpotifyMediaItem, + modifier: Modifier = Modifier, + index: Int? = null, + showArt: Boolean = true, + secondary: String = item.creator, + trailing: String? = formatDuration(item.durationMs), + onClick: () -> Unit +) { + val colors = SidekitTheme.colors + SidekitTile( + modifier = modifier.fillMaxWidth(), + color = if (item.playable) colors.surfaceA3 else colors.surfaceA2, + onClick = onClick + ) { + Row( + Modifier.fillMaxWidth().padding(horizontal = 15.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(13.dp) + ) { + if (showArt) { + SpotifyCover(item.artRes, Modifier.size(68.dp)) + } else if (index != null) { + Box(Modifier.size(30.dp), contentAlignment = Alignment.Center) { + SidekitText("${index + 1}", style = SidekitTheme.typography.label, color = colors.surfaceA6) + } + } + Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { + SidekitText(item.title, style = SidekitTheme.typography.bodySmall, maxLines = 1) + SidekitText( + secondary + if (item.explicit) " · E" else "", + style = SidekitTheme.typography.caption, + color = colors.surfaceA6, + maxLines = 1 + ) + } + if (trailing != null) { + SidekitText( + trailing, + style = SidekitTheme.typography.caption, + color = colors.surfaceA6 + ) + } + } + } +} + +@Composable +fun RowScope.SpotifyIconTile( + icon: SidekitIcon, + label: String, + onClick: () -> Unit, + tone: SpotifyTileTone = SpotifyTileTone.Neutral, + weight: Float = 1f, + modifier: Modifier = Modifier +) { + val colors = SidekitTheme.colors + SidekitIconItem( + icon, + label, + modifier.weight(weight), + color = tone.colour(colors), + iconSize = SidekitTheme.spacing.tileIconSize, + onClick = onClick + ) +} + +enum class SpotifyTileTone { + Neutral, + Play, + On, + Muted + ; + + fun colour(colors: hrln.interactive.sidekit.design.theme.SidekitColors): Color = when (this) { + Neutral -> colors.surfaceA3 + Play -> colors.emphasisA1 + On -> colors.surfaceA5 + Muted -> colors.surfaceA2 + } +} + +@Composable +fun NowPlayingTile( + current: SpotifyMediaItem?, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + val colors = SidekitTheme.colors + SidekitTile(modifier, onClick = onClick, onClickLabel = "Now Playing") { + Row( + Modifier.fillMaxWidth().padding(horizontal = 14.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(13.dp) + ) { + if (current == null) { + Box(Modifier.size(72.dp).clip(SidekitTheme.shapes.control).background(colors.surfaceA2), contentAlignment = Alignment.Center) { + SidekitIconImage(SidekitIcon.MusicNote2, size = 36.dp) + } + } else { + SpotifyCover(current.artRes, Modifier.size(72.dp)) + } + Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(1.dp)) { + SidekitText( + if (current == null) "NOTHING PLAYING" else "PLAYING", + style = SidekitTheme.typography.small, + color = colors.surfaceA6 + ) + SidekitText(current?.title ?: "Pick a track", style = SidekitTheme.typography.bodySmall, maxLines = 2) + } + } + } +} + +@Composable +fun LibraryTile(onClick: () -> Unit, modifier: Modifier = Modifier) { + SidekitTile(modifier, onClick = onClick, onClickLabel = "Library") { + Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp)) { + SidekitIconImage(SidekitIcon.PhotoLibrary, size = SidekitTheme.spacing.tileIconSize) + SidekitText("Library", style = SidekitTheme.typography.bodySmall) + } + } +} + +@Composable +fun ArtworkTile(@DrawableRes artRes: Int, label: String, modifier: Modifier = Modifier) { + SidekitTile(modifier) { + SpotifyCover(artRes, Modifier.fillMaxSize(), shape = SidekitTheme.shapes.item, contentDescription = label) + } +} + +@Composable +fun EmptyMessage(title: String, hint: String? = null, modifier: Modifier = Modifier) { + Column( + modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 18.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + SidekitText(title, style = SidekitTheme.typography.bodySmall, color = SidekitTheme.colors.surfaceA6) + if (hint != null) SidekitText(hint, style = SidekitTheme.typography.caption, color = SidekitTheme.colors.surfaceA6) + } +} + +@Composable +fun TerminalRow(label: String, secondary: String? = null, onClick: (() -> Unit)? = null) { + SidekitItem( + label, + secondary = secondary, + showLinkArrow = onClick != null, + colour = hrln.interactive.sidekit.design.components.ItemColour.Secondary, + onClick = onClick + ) +} + +fun withNotice(notice: String?, onDismiss: () -> Unit, actions: List): List = if (notice == null) { + actions +} else { + listOf( + SideAction( + "notice", + notice, + tone = ActionTone.Muted, + dense = true, + motion = ActionMotion.Up, + onClick = onDismiss + ) + ) +} + +fun spotifyBack(label: String, onBack: () -> Unit) = listOf(SideAction.back("back", label, onBack)) + +fun repeatLabel(repeat: RepeatMode) = repeat.label + +/** + * The seek row: tap or drag, or step with D-pad left/right. It mirrors Music's seek row so the + * transport feels the same in both apps. + */ +@Composable +fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modifier: Modifier = Modifier) { + val seekable = durationMs > 0 + val fraction = if (seekable) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f + val trackColor = SidekitTheme.colors.surfaceA4 + val progressColor = SidekitTheme.colors.light + val labelStyle = SidekitTheme.typography.label + val timeStyle = remember(labelStyle) { labelStyle.copy(fontFeatureSettings = "tnum") } + val widest = remember(durationMs) { formatDuration(durationMs).replace(Regex("\\d"), "0") } + val interaction = remember { MutableInteractionSource() } + fun seekTo(target: Float) { + if (seekable) onSeek((target.coerceIn(0f, 1f) * durationMs).toLong()) + } + val step = if (seekable) 5_000f / durationMs else 0f + Row(modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Box(contentAlignment = Alignment.CenterEnd) { + SidekitText(widest, Modifier.alpha(0f), style = timeStyle) + SidekitText(formatDuration(positionMs), style = timeStyle) + } + Box( + Modifier + .weight(1f) + .height(36.dp) + .onKeyEvent { event -> + if (event.type != KeyEventType.KeyDown) return@onKeyEvent false + when (event.key) { + Key.DirectionLeft -> { + seekTo(fraction - step) + true + } + + Key.DirectionRight -> { + seekTo(fraction + step) + true + } + + else -> false + } + } + .indication(interaction, LocalIndication.current) + .focusable(interactionSource = interaction) + .pointerInput(durationMs) { detectTapGestures { offset -> seekTo(offset.x / size.width) } } + .pointerInput(durationMs) { detectHorizontalDragGestures { change, _ -> seekTo(change.position.x / size.width) } } + .semantics { + if (seekable) { + contentDescription = "Seek" + progressBarRangeInfo = ProgressBarRangeInfo(fraction, 0f..1f) + setProgress { target -> + seekTo(target) + true + } + } + } + .drawBehind { + val h = 6.dp.toPx() + val y = (size.height - h) / 2 + drawRoundRect(trackColor, Offset(0f, y), Size(size.width, h), CornerRadius(h / 2)) + if (fraction > 0f) { + val filled = size.width * fraction + drawRoundRect(progressColor, Offset(0f, y), Size(filled, h), CornerRadius(h / 2)) + drawCircle(progressColor, 9.dp.toPx(), Offset(filled, size.height / 2)) + } + } + ) + SidekitText(formatDuration(durationMs), style = timeStyle) + } +} + +@Composable +fun PlayTile(onClick: () -> Unit, modifier: Modifier = Modifier) { + SidekitIconItem( + SidekitIcon.PlayArrow, + "Play", + modifier, + color = SidekitTheme.colors.emphasisA1, + iconSize = SidekitTheme.spacing.tileIconSize, + onClick = onClick + ) +} + +@Composable +fun SpotifyLibraryRow( + @DrawableRes artRes: Int, + title: String, + secondary: String, + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + val colors = SidekitTheme.colors + SidekitTile(modifier.fillMaxWidth(), color = colors.surfaceA3, onClick = onClick) { + Row( + Modifier.fillMaxWidth().padding(horizontal = 15.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(13.dp) + ) { + SpotifyCover(artRes, Modifier.size(68.dp)) + Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { + SidekitText(title, style = SidekitTheme.typography.bodySmall, maxLines = 1) + SidekitText(secondary, style = SidekitTheme.typography.caption, color = colors.surfaceA6, maxLines = 1) + } + SidekitIconImage(SidekitIcon.ArrowRightAlt, size = 30.dp, contentDescription = null) + } + } +} + +/** Compact "Up next" affordance; a full Item would steal height from the artwork. */ +@Composable +fun UpNextRow(title: String, modifier: Modifier = Modifier, onClick: () -> Unit) { + val colors = SidekitTheme.colors + Row( + modifier + .fillMaxWidth() + .height(46.dp) + .clip(SidekitTheme.shapes.control) + .background(colors.surfaceA3) + .clickable(onClickLabel = "Open queue", onClick = onClick) + .padding(horizontal = 18.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + SidekitText("Up next", style = SidekitTheme.typography.caption, color = colors.surfaceA6) + SidekitText(title, Modifier.weight(1f), style = SidekitTheme.typography.caption, maxLines = 1) + SidekitIconImage(SidekitIcon.ArrowRightAlt, size = 26.dp) + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt new file mode 100644 index 00000000..e20b0027 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -0,0 +1,244 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import androidx.navigation.NavBackStackEntry +import androidx.navigation.NavGraphBuilder +import androidx.navigation.NavHostController +import androidx.navigation.NavType +import androidx.navigation.compose.composable +import androidx.navigation.compose.navigation +import androidx.navigation.navArgument +import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.navigation.LAUNCHER_ROUTE +import hrln.interactive.sidekit.navigation.SubApp + +private val ROOT = SubApp.Sidetracks.route +private val HOME = "$ROOT/home" +private val LIBRARY = "$ROOT/library" +private val COLLECTION = "$ROOT/collection/{type}/{id}" +private val ARTIST = "$ROOT/artist/{id}" +private val SEARCH = "$ROOT/search" +private val NOW_PLAYING = "$ROOT/now-playing" +private val QUEUE = "$ROOT/queue" +private val SETTINGS = "$ROOT/settings" +private val SIGN_IN = "$ROOT/sign-in" + +private fun collectionRoute(type: SpotifyCollectionType, id: String) = "$ROOT/collection/${type.route}/$id" +private fun artistRoute(id: String) = "$ROOT/artist/$id" + +private val collectionArgs = listOf( + navArgument("type") { type = NavType.StringType }, + navArgument("id") { type = NavType.StringType } +) +private val artistArgs = listOf(navArgument("id") { type = NavType.StringType }) + +/** Registers Sidetracks as one Sidekit sub-app. All screens share one mock account state holder. */ +fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { + navigation(startDestination = HOME, route = ROOT) { + composable(HOME) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + LaunchedEffect(state.signedIn) { + if (!state.signedIn) nav.navigate(SIGN_IN) { launchSingleTop = true } + } + SidetracksHomeScreen( + state, + onNowPlaying = { nav.navigate(NOW_PLAYING) }, + onLibrary = { nav.navigate(LIBRARY) }, + onSearch = { nav.navigate(SEARCH) }, + onSettings = { nav.navigate(SETTINGS) }, + onPlay = vm::playLibraryTrack, + onDismissNotice = vm::dismissNotice + ) + } + composable(LIBRARY) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + LibraryScreen( + state, + onBack = { nav.popBackStack() }, + onSearch = { nav.navigate(SEARCH) }, + onFilter = vm::setFilter, + onTrack = vm::playItem, + onPlaylist = { nav.navigate(collectionRoute(SpotifyCollectionType.Playlist, it.id)) }, + onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, + onArtist = { nav.navigate(artistRoute(it.id)) }, + onShow = { nav.navigate(collectionRoute(SpotifyCollectionType.Show, it.id)) }, + onLoadMore = { vm.notice("Everything in this filter is already loaded") }, + onDismissNotice = vm::dismissNotice + ) + } + composable(COLLECTION, arguments = collectionArgs) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + val type = SpotifyCollectionType.entries.firstOrNull { it.route == entry.arguments?.getString("type") } + val id = entry.arguments?.getString("id").orEmpty() + val collection = type?.let { MockSpotifyDataSource.collection(it, id) } + if (collection == null) { + MissingCollectionScreen(onBack = { nav.popBackStack() }) + } else { + CollectionScreen( + collection, + state, + onBack = { nav.popBackStack() }, + onPlay = { vm.playCollection(collection, 0) }, + onItem = { vm.playCollection(collection, it) }, + onSave = { vm.toggleSaved(collection) }, + onArtist = { + collection.artistId?.let { nav.navigate(artistRoute(it)) } ?: vm.notice("This collection has no artist page") + }, + onQueue = { collection.items.firstOrNull()?.let(vm::addToQueue) }, + onNowPlaying = { nav.navigate(NOW_PLAYING) }, + onLoadMore = { vm.notice("Everything in this collection is already loaded") }, + onDismissNotice = vm::dismissNotice + ) + } + } + composable(ARTIST, arguments = artistArgs) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + val artist = MockSpotifyDataSource.artist(entry.arguments?.getString("id").orEmpty()) + if (artist == null) { + MissingCollectionScreen(onBack = { nav.popBackStack() }) + } else { + val albums = MockSpotifyDataSource.albumsBy(artist) + val tracks = MockSpotifyDataSource.topTracks(artist) + ArtistScreen( + artist, + albums, + tracks, + state, + onBack = { nav.popBackStack() }, + onPlay = { vm.playArtist(artist) }, + onTrack = { vm.play(tracks, tracks.indexOf(it)) }, + onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, + onFollow = { vm.toggleFollow(artist) }, + onNowPlaying = { nav.navigate(NOW_PLAYING) }, + onDismissNotice = vm::dismissNotice + ) + } + } + composable(SEARCH) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + SpotifySearchScreen( + state, + onBack = { nav.popBackStack() }, + onQuery = vm::setQuery, + onClear = vm::clearQuery, + onToggleScope = vm::toggleScope, + onTrack = vm::playItem, + onArtist = { nav.navigate(artistRoute(it.id)) }, + onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, + onPlaylist = { nav.navigate(collectionRoute(SpotifyCollectionType.Playlist, it.id)) }, + onShow = { nav.navigate(collectionRoute(SpotifyCollectionType.Show, it.id)) }, + onDismissNotice = vm::dismissNotice + ) + } + composable(NOW_PLAYING) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + SpotifyNowPlayingScreen( + state, + onBack = { nav.popBackStack() }, + onPlayPause = vm::togglePlayPause, + onPrevious = vm::previous, + onNext = vm::next, + onSeek = vm::seekTo, + onShuffle = vm::toggleShuffle, + onRepeat = vm::cycleRepeat, + onSave = vm::toggleSaveCurrent, + onQueue = { nav.navigate(QUEUE) }, + onAlbum = { + (state.current as? SpotifyTrack)?.let { track -> + nav.navigate(collectionRoute(SpotifyCollectionType.Album, track.albumId)) + } + }, + onArtist = { + state.current?.artistId?.let { nav.navigate(artistRoute(it)) } + }, + onAddToPlaylist = vm::addCurrentToPlaylist, + onDismissNotice = vm::dismissNotice + ) + } + composable(QUEUE) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + QueueScreen( + state, + onBack = { nav.popBackStack() }, + onNowPlaying = { nav.popBackStack(NOW_PLAYING, inclusive = false) }, + onJump = vm::jumpTo, + onPlayNext = vm::moveToNext, + onRemove = vm::removeUpcoming, + onClearUpcoming = vm::clearUpcoming, + onRestoreUpcoming = vm::restoreUpcoming, + onShuffle = vm::toggleShuffle, + onDismissNotice = vm::dismissNotice + ) + } + composable(SETTINGS) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + SpotifySettingsScreen( + state, + onBack = { nav.popBackStack() }, + onAudioQuality = vm::cycleAudioQuality, + onVolume = vm::setVolume, + onClearCache = vm::clearCache, + onClearHistory = vm::clearHistory, + onSignOut = { + vm.signOut() + nav.navigate(SIGN_IN) { launchSingleTop = true } + }, + onNotice = vm::notice, + onDismissNotice = vm::dismissNotice + ) + } + composable(SIGN_IN) { entry -> + val vm = entry.spotifyViewModel(nav) + val state by vm.state.collectAsStateWithLifecycle() + SpotifySignInScreen( + state, + onBack = { nav.popBackStack(LAUNCHER_ROUTE, inclusive = false) }, + onLogIn = { + vm.signIn() + nav.navigate(HOME) { + popUpTo(HOME) { inclusive = false } + launchSingleTop = true + } + }, + onCopyLink = { vm.notice("Login link copied") }, + onDismissNotice = vm::dismissNotice + ) + } + } +} + +@Composable +private fun MissingCollectionScreen(onBack: () -> Unit) { + SidekitScaffold(title = "Sidetracks", actions = spotifyBack("Back", onBack)) { padding -> + androidx.compose.foundation.layout.Box( + androidx.compose.ui.Modifier.padding(padding) + ) { + EmptyMessage("This collection isn't available", "Go back and choose another") + } + } +} + +@Composable +private fun NavBackStackEntry.spotifyViewModel(nav: NavHostController): SpotifyViewModel { + val parent = remember(this) { nav.getBackStackEntry(ROOT) } + return viewModel( + parent, + factory = remember { viewModelFactory { initializer { SpotifyViewModel() } } } + ) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt new file mode 100644 index 00000000..9370af00 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -0,0 +1,306 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.design.action.ActionMotion +import hrln.interactive.sidekit.design.action.ActionTone +import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.components.SidekitItem +import hrln.interactive.sidekit.design.components.SidekitItemRow +import hrln.interactive.sidekit.design.components.SidekitList +import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.icons.SidekitIcon +import hrln.interactive.sidekit.design.theme.SidekitTheme + +@Composable +fun SidetracksHomeScreen( + state: SpotifyUiState, + onNowPlaying: () -> Unit, + onLibrary: () -> Unit, + onSearch: () -> Unit, + onSettings: () -> Unit, + onPlay: (SpotifyTrack) -> Unit, + onDismissNotice: () -> Unit +) { + val recent = MockSpotifyDataSource.recentlyPlayed.take(10) + val bar = withNotice( + state.notice, + onDismissNotice, + listOf( + SideAction( + "nowPlaying", + square = true, + contentDescription = "Now Playing", + artKey = state.current?.id, + art = { + if (state.current != null) { + SpotifyCover(state.current.artRes, Modifier.height(SidekitTheme.spacing.tileIconSize)) + } else { + hrln.interactive.sidekit.design.components.SidekitIconImage(SidekitIcon.MusicNote2) + } + }, + onClick = onNowPlaying + ), + SideAction("search", "Search", SidekitIcon.Search, motion = ActionMotion.Forward, onClick = onSearch) + ) + ) + SidekitScaffold( + title = "Sidetracks", + actions = bar, + options = listOf(SideAction("settings", "Settings", SidekitIcon.Settings, onClick = onSettings)) + ) { padding -> + val listState = rememberLazyListState() + SidekitList(padding, state = listState) { + item(key = "tiles") { + SidekitItemRow(Modifier.listFade(listState, 0)) { + NowPlayingTile(state.current, onNowPlaying, Modifier.weight(1.38f)) + LibraryTile(onLibrary, Modifier.weight(1f)) + } + } + item(key = "recentLabel") { SectionLabel("Recently played") } + if (recent.isEmpty()) { + item(key = "empty") { EmptyMessage("Nothing played yet", "Search to find your first track") } + } else { + itemsIndexed(recent, key = { _, track -> track.id }) { index, track -> + SpotifyMediaRow( + track, + Modifier.listFade(listState, index + 2), + secondary = track.artist, + onClick = { onPlay(track) } + ) + } + } + } + } +} + +@Composable +fun LibraryScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onSearch: () -> Unit, + onFilter: (LibraryFilter) -> Unit, + onTrack: (SpotifyTrack) -> Unit, + onPlaylist: (SpotifyPlaylist) -> Unit, + onAlbum: (SpotifyAlbum) -> Unit, + onArtist: (SpotifyArtist) -> Unit, + onShow: (SpotifyShow) -> Unit, + onLoadMore: () -> Unit, + onDismissNotice: () -> Unit +) { + val colors = SidekitTheme.colors + val actions = withNotice( + state.notice, + onDismissNotice, + listOf(spotifyBack("Sidetracks", onBack).first(), SideAction("search", "Search", SidekitIcon.Search, motion = ActionMotion.Forward, onClick = onSearch)) + ) + val filters = LibraryFilter.entries.map { filter -> + SideAction( + "filter.${filter.name}", + filter.label, + tone = if (filter == state.filter) ActionTone.Primary else ActionTone.Muted, + onClick = { onFilter(filter) } + ) + } + SidekitScaffold( + title = null, + header = { + Column( + Modifier.fillMaxWidth().height(SidekitTheme.spacing.headerHeight) + .padding(horizontal = SidekitTheme.spacing.itemPadding).padding(top = 14.dp), + verticalArrangement = Arrangement.spacedBy(0.dp) + ) { + SidekitText("Library", style = SidekitTheme.typography.headline) + SidekitText(state.filter.label, style = SidekitTheme.typography.caption, color = colors.surfaceA6) + } + }, + actions = actions, + options = filters + ) { padding -> + val listState = rememberLazyListState() + val first = remember { FocusRequester() } + LaunchedEffect(state.filter) { runCatching { first.requestFocus() } } + SidekitList(padding, state = listState) { + val items = MockSpotifyDataSource.library(state.filter) + if (items.isEmpty()) { + item(key = "empty") { EmptyMessage(state.filter.empty, "Search to find something to save") } + } + itemsIndexed(items, key = { index, item -> "$index:${libraryKey(item)}" }) { index, item -> + val modifier = Modifier.listFade(listState, index).then(if (index == 0) Modifier.focusRequester(first) else Modifier) + when (item) { + is SpotifyTrack -> SpotifyMediaRow(item, modifier, secondary = item.artist, onClick = { onTrack(item) }) + is SpotifyPlaylist -> SpotifyLibraryRow(item.artRes, item.name, "${item.owner} · ${item.tracks.size} tracks", modifier) { onPlaylist(item) } + is SpotifyAlbum -> SpotifyLibraryRow(item.artRes, item.title, "${item.artist} · ${item.year}", modifier) { onAlbum(item) } + is SpotifyArtist -> SpotifyLibraryRow(item.artRes, item.name, "${item.albumIds.size} albums", modifier) { onArtist(item) } + is SpotifyShow -> SpotifyLibraryRow(item.artRes, item.name, item.publisher, modifier) { onShow(item) } + else -> Unit + } + } + if (items.isNotEmpty()) { + item(key = "more") { + TerminalRow( + "Load more", + secondary = when (state.filter) { + LibraryFilter.LikedSongs -> "${items.size} liked songs" + LibraryFilter.Playlists -> "${items.size} playlists" + LibraryFilter.Albums -> "${items.size} albums" + LibraryFilter.Artists -> "${items.size} artists" + LibraryFilter.Shows -> "${items.size} shows" + }, + onClick = onLoadMore + ) + } + } + } + } +} + +@Composable +fun CollectionScreen( + collection: SpotifyCollection, + state: SpotifyUiState, + onBack: () -> Unit, + onPlay: () -> Unit, + onItem: (Int) -> Unit, + onSave: () -> Unit, + onArtist: () -> Unit, + onQueue: () -> Unit, + onNowPlaying: () -> Unit, + onLoadMore: () -> Unit, + onDismissNotice: () -> Unit +) { + val colors = SidekitTheme.colors + val saved = "${collection.type.route}:${collection.id}" in state.savedCollectionKeys + val actions = withNotice(state.notice, onDismissNotice, spotifyBack("Back", onBack)) + val options = buildList { + add( + SideAction( + "save", + if (saved) "Saved" else "Save", + if (saved) SidekitIcon.Check else SidekitIcon.Bookmark, + tone = if (saved) ActionTone.Secondary else ActionTone.Primary, + onClick = onSave + ) + ) + if (collection.artistId != null) add(SideAction("artist", "Artist", SidekitIcon.Artist, onClick = onArtist)) + collection.items.firstOrNull()?.let { add(SideAction("queue", "Add to queue", SidekitIcon.QueueMusic, onClick = onQueue)) } + add(SideAction("nowPlaying", "Go to now playing", SidekitIcon.MusicNote2, onClick = onNowPlaying)) + } + SidekitScaffold(title = collection.title, actions = actions, options = options) { padding -> + val listState = rememberLazyListState() + val playFocus = remember { FocusRequester() } + LaunchedEffect(collection.type, collection.id) { runCatching { playFocus.requestFocus() } } + SidekitList(padding, state = listState) { + item(key = "tiles") { + SidekitItemRow(Modifier.listFade(listState, 0)) { + ArtworkTile(collection.artRes, "${collection.title} artwork", Modifier.weight(1f)) + PlayTile(onPlay, Modifier.weight(1f).focusRequester(playFocus)) + SpotifyIconTile( + if (saved) SidekitIcon.Check else SidekitIcon.Bookmark, + if (saved) "Saved" else "Save", + onSave, + if (saved) SpotifyTileTone.On else SpotifyTileTone.Neutral + ) + if (collection.artistId != null) { + SpotifyIconTile(SidekitIcon.Artist, "Artist", onArtist) + } + } + } + item(key = "meta") { + SidekitText( + collection.subtitle, + Modifier.padding(start = 18.dp, end = 18.dp), + style = SidekitTheme.typography.caption, + color = colors.surfaceA6, + maxLines = 1 + ) + } + itemsIndexed(collection.items, key = { _, item -> item.id }) { index, item -> + SpotifyMediaRow( + item, + Modifier.listFade(listState, index + 2), + index = index, + showArt = false, + secondary = if (item is SpotifyEpisode) item.published else item.creator, + onClick = { onItem(index) } + ) + } + item(key = "more") { TerminalRow("Load more", onClick = onLoadMore) } + } + } +} + +@Composable +fun ArtistScreen( + artist: SpotifyArtist, + albums: List, + tracks: List, + state: SpotifyUiState, + onBack: () -> Unit, + onPlay: () -> Unit, + onTrack: (SpotifyTrack) -> Unit, + onAlbum: (SpotifyAlbum) -> Unit, + onFollow: () -> Unit, + onNowPlaying: () -> Unit, + onDismissNotice: () -> Unit +) { + val followed = artist.id in state.followedArtistIds + val actions = withNotice(state.notice, onDismissNotice, spotifyBack("Back", onBack)) + SidekitScaffold( + title = artist.name, + actions = actions, + options = listOf( + SideAction("follow", if (followed) "Following" else "Follow", tone = if (followed) ActionTone.Secondary else ActionTone.Primary, onClick = onFollow), + SideAction("nowPlaying", "Go to now playing", SidekitIcon.MusicNote2, onClick = onNowPlaying) + ) + ) { padding -> + val listState = rememberLazyListState() + SidekitList(padding, state = listState) { + item(key = "tiles") { + SidekitItemRow(Modifier.listFade(listState, 0)) { + PlayTile(onPlay, Modifier.weight(1f)) + ArtworkTile(artist.artRes, "${artist.name} artwork", Modifier.weight(1f)) + } + } + item(key = "topLabel") { SectionLabel("Top tracks") } + itemsIndexed(tracks.take(10), key = { _, track -> track.id }) { index, track -> + SpotifyMediaRow(track, Modifier.listFade(listState, index + 2), secondary = track.artist, onClick = { onTrack(track) }) + } + item(key = "albumsLabel") { SectionLabel("Albums") } + itemsIndexed(albums, key = { _, album -> album.id }) { index, album -> + SpotifyLibraryRow( + album.artRes, + album.title, + "${album.year} · ${album.tracks.size} tracks", + Modifier.listFade(listState, index + tracks.size + 3) + ) { onAlbum(album) } + } + } + } +} + +private fun libraryKey(item: Any): String = when (item) { + is SpotifyTrack -> "t${item.id}" + is SpotifyPlaylist -> "p${item.id}" + is SpotifyAlbum -> "a${item.id}" + is SpotifyArtist -> "r${item.id}" + is SpotifyShow -> "s${item.id}" + else -> item.toString() +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt new file mode 100644 index 00000000..5a59f1dd --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt @@ -0,0 +1,153 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.annotation.DrawableRes + +/** A saved album, playlist or show. Collections share one screen, so they share one shape. */ +enum class SpotifyCollectionType(val route: String) { + Album("album"), + Playlist("playlist"), + Show("show") +} + +/** + * Everything that can be played. Albums and playlists hold [TrackItem]s, shows hold + * [EpisodeItem]s; the queue and Now Playing never need to know which screen it came from. + */ +sealed interface SpotifyMediaItem { + val id: String + val title: String + val creator: String + val artistId: String? + val artRes: Int + val durationMs: Long + val playable: Boolean + val explicit: Boolean +} + +data class SpotifyTrack( + override val id: String, + override val title: String, + val artist: String, + override val artistId: String, + val albumId: String, + override val artRes: Int, + override val durationMs: Long, + override val explicit: Boolean = false, + override val playable: Boolean = true +) : SpotifyMediaItem { + override val creator: String get() = artist +} + +data class SpotifyEpisode( + override val id: String, + override val title: String, + val show: String, + val showId: String, + val published: String, + override val artRes: Int, + override val durationMs: Long, + val video: Boolean = false +) : SpotifyMediaItem { + override val creator: String get() = show + override val artistId: String? get() = null + override val playable: Boolean get() = !video + override val explicit: Boolean get() = false +} + +data class SpotifyAlbum( + val id: String, + val title: String, + val artist: String, + val artistId: String, + val year: Int, + @param:DrawableRes val artRes: Int, + val tracks: List +) + +data class SpotifyPlaylist( + val id: String, + val name: String, + val owner: String, + val ownerId: String?, + @param:DrawableRes val artRes: Int, + val tracks: List +) + +data class SpotifyShow( + val id: String, + val name: String, + val publisher: String, + @param:DrawableRes val artRes: Int, + val episodes: List +) + +data class SpotifyArtist( + val id: String, + val name: String, + @param:DrawableRes val artRes: Int, + val albumIds: List +) + +/** One screen's worth of a collection, already resolved by the gateway. */ +data class SpotifyCollection( + val type: SpotifyCollectionType, + val id: String, + val title: String, + val subtitle: String, + val artRes: Int, + val items: List, + val artistId: String? = null +) + +data class SpotifySearchResults( + val tracks: List = emptyList(), + val artists: List = emptyList(), + val albums: List = emptyList(), + val playlists: List = emptyList(), + val shows: List = emptyList() +) { + val isEmpty: Boolean get() = tracks.isEmpty() && artists.isEmpty() && albums.isEmpty() && playlists.isEmpty() && shows.isEmpty() +} + +enum class LibraryFilter(val label: String, val empty: String) { + LikedSongs("Liked songs", "No liked songs"), + Playlists("Playlists", "No saved playlists"), + Albums("Albums", "No saved albums"), + Artists("Artists", "No followed artists"), + Shows("Shows", "No saved shows") +} + +enum class SearchScope(val label: String) { + Tracks("Tracks"), + Artists("Artists"), + Albums("Albums"), + Playlists("Playlists"), + Shows("Shows") +} + +enum class RepeatMode(val label: String) { + Off("Repeat: off"), + Context("Repeat: context"), + Track("Repeat: track") + ; + + fun next(): RepeatMode = when (this) { + Off -> Context + Context -> Track + Track -> Off + } +} + +enum class AudioQuality(val label: String) { + Normal("Normal"), + High("High"), + VeryHigh("Very high") + ; + + fun next(): AudioQuality = entries[(ordinal + 1) % entries.size] +} + +data class SpotifyQueueItem( + val item: SpotifyMediaItem, + val isCurrent: Boolean = false +) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt new file mode 100644 index 00000000..4d9028bc --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -0,0 +1,248 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.design.action.ActionMotion +import hrln.interactive.sidekit.design.action.ActionTone +import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.action.undoBar +import hrln.interactive.sidekit.design.components.SidekitItemRow +import hrln.interactive.sidekit.design.components.SidekitList +import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.icons.SidekitIcon +import hrln.interactive.sidekit.design.theme.SidekitTheme +import kotlinx.coroutines.delay + +@Composable +fun SpotifyNowPlayingScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onPlayPause: () -> Unit, + onPrevious: () -> Unit, + onNext: () -> Unit, + onSeek: (Long) -> Unit, + onShuffle: () -> Unit, + onRepeat: () -> Unit, + onSave: () -> Unit, + onQueue: () -> Unit, + onAlbum: () -> Unit, + onArtist: () -> Unit, + onAddToPlaylist: () -> Unit, + onDismissNotice: () -> Unit +) { + val current = state.current + val actions = withNotice( + state.notice, + onDismissNotice, + buildList { + add(spotifyBack("Back", onBack).first()) + if (current != null) { + add( + SideAction.square( + "save", + if (state.savedCurrent) SidekitIcon.Check else SidekitIcon.Bookmark, + if (state.savedCurrent) "Saved" else "Save", + onSave, + tone = if (state.savedCurrent) ActionTone.Secondary else ActionTone.Muted, + motion = ActionMotion.Up + ) + ) + } + } + ) + val options = if (current == null) { + emptyList() + } else { + buildList { + add(SideAction("queue", "Queue", SidekitIcon.QueueMusic, onClick = onQueue)) + if (current is SpotifyTrack) add(SideAction("album", "Album", SidekitIcon.Album, onClick = onAlbum)) + if (current.artistId != null) add(SideAction("artist", "Artist", SidekitIcon.Artist, onClick = onArtist)) + add(SideAction("playlist", "Add to playlist", SidekitIcon.Add, onClick = onAddToPlaylist)) + } + } + SidekitScaffold(title = null, actions = actions, options = options) { padding -> + if (current == null) { + Box(Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) { + EmptyMessage("Nothing playing", "Pick something from your library") + } + } else { + Column( + Modifier.fillMaxSize().padding(padding), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + BoxWithConstraints(Modifier.weight(1f).fillMaxWidth(), contentAlignment = Alignment.Center) { + val art = minOf(maxWidth, maxHeight) + SpotifyCover( + current.artRes, + Modifier.size(art), + shape = SidekitTheme.shapes.item, + bordered = true, + contentDescription = "${current.title} artwork" + ) + } + Column(horizontalAlignment = Alignment.CenterHorizontally) { + SidekitText( + current.title, + Modifier.fillMaxWidth(), + style = SidekitTheme.typography.headline, + textAlign = TextAlign.Center, + minLines = 1, + maxLines = 2 + ) + if (current.creator.isNotBlank()) { + SidekitText( + current.creator, + Modifier.fillMaxWidth().clickable(enabled = current.artistId != null, onClickLabel = "Open artist", onClick = onArtist), + style = SidekitTheme.typography.label, + color = SidekitTheme.colors.surfaceA6, + textAlign = TextAlign.Center + ) + } + } + SpotifySeek(state.positionMs, current.durationMs, onSeek) + SidekitItemRow { + SpotifyIconTile( + SidekitIcon.Shuffle, + if (state.shuffle) "Shuffle on" else "Shuffle off", + onShuffle, + if (state.shuffle) SpotifyTileTone.On else SpotifyTileTone.Neutral, + 0.85f + ) + SpotifyIconTile(SidekitIcon.SkipPrevious, "Previous", onPrevious, weight = 0.85f) + SpotifyIconTile( + if (state.isPlaying) SidekitIcon.Pause else SidekitIcon.PlayArrow, + if (state.isPlaying) "Pause" else "Play", + onPlayPause, + SpotifyTileTone.Play, + 1.2f + ) + SpotifyIconTile(SidekitIcon.SkipNext, "Next", onNext, weight = 0.85f) + SpotifyIconTile( + SidekitIcon.Undo, + state.repeat.label, + onRepeat, + if (state.repeat == RepeatMode.Off) SpotifyTileTone.Neutral else SpotifyTileTone.On, + 0.85f + ) + } + state.upcoming.firstOrNull()?.let { UpNextRow(it.title, onClick = onQueue) } + } + } + } +} + +@Composable +fun QueueScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onNowPlaying: () -> Unit, + onJump: (String) -> Unit, + onPlayNext: (SpotifyMediaItem) -> Unit, + onRemove: (SpotifyMediaItem) -> Unit, + onClearUpcoming: () -> List, + onRestoreUpcoming: (List) -> Unit, + onShuffle: () -> Unit, + onDismissNotice: () -> Unit +) { + var focusedId by remember { mutableStateOf(state.current?.id) } + var pendingUndo by remember { mutableStateOf?>(null) } + LaunchedEffect(pendingUndo) { + if (pendingUndo != null) { + delay(5_000L) + pendingUndo = null + } + } + val back = spotifyBack("Back", onBack).first() + val normal = withNotice( + state.notice, + onDismissNotice, + listOf(back, SideAction("nowPlaying", "Now playing", SidekitIcon.MusicNote2, onClick = onNowPlaying)) + ) + val actions = if (pendingUndo != null) { + undoBar( + back, + SideAction.undo("undoClear", "Undo", "Undo clear upcoming") { + onRestoreUpcoming(pendingUndo.orEmpty()) + pendingUndo = null + } + ) + } else { + normal + } + val focused = (listOfNotNull(state.current) + state.upcoming + state.history).firstOrNull { it.id == focusedId } + val options = buildList { + if (focused != null && focused.id != state.current?.id && focused.id !in state.history.map { it.id }) { + add(SideAction("playNext", "Play next", SidekitIcon.ArrowUpward, onClick = { onPlayNext(focused) })) + add(SideAction("remove", "Remove", SidekitIcon.DeleteForever, tone = ActionTone.Destructive, onClick = { onRemove(focused) })) + } + add(SideAction("clear", "Clear upcoming", SidekitIcon.DeleteForever, tone = ActionTone.Destructive, onClick = { pendingUndo = onClearUpcoming() })) + add(SideAction("shuffle", if (state.shuffle) "Shuffle on" else "Shuffle off", SidekitIcon.Shuffle, onClick = onShuffle)) + } + SidekitScaffold(title = "Queue", actions = actions, options = options) { padding -> + val listState = rememberLazyListState() + val currentFocus = remember { FocusRequester() } + LaunchedEffect(state.current?.id) { if (focusedId == null) runCatching { currentFocus.requestFocus() } } + SidekitList(padding, state = listState) { + val current = state.current + if (current == null) { + item(key = "empty") { EmptyMessage("Nothing queued", "Search to find something to play") } + } else { + item(key = "current") { + SpotifyMediaRow( + current, + Modifier.listFade(listState, 0).focusRequester(currentFocus).onFocusChanged { if (it.isFocused) focusedId = current.id }, + secondary = "Now playing", + onClick = { onJump(current.id) } + ) + } + itemsIndexed(state.upcoming, key = { _, item -> "u${item.id}" }) { index, item -> + SpotifyMediaRow( + item, + Modifier.listFade(listState, index + 1).onFocusChanged { if (it.isFocused) focusedId = item.id }, + secondary = item.creator, + onClick = { onJump(item.id) } + ) + } + if (state.history.isNotEmpty()) { + item(key = "historyLabel") { SectionLabel("History") } + itemsIndexed(state.history.take(20), key = { _, item -> "h${item.id}" }) { index, item -> + SpotifyMediaRow( + item, + Modifier.listFade(listState, index + 2 + state.upcoming.size), + secondary = item.creator, + onClick = { onJump(item.id) } + ) + } + } + } + } + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt new file mode 100644 index 00000000..5abc0231 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -0,0 +1,144 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.design.action.ActionMotion +import hrln.interactive.sidekit.design.action.ActionTone +import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.components.SidekitList +import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitTextField +import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.icons.SidekitIcon +import hrln.interactive.sidekit.design.theme.SidekitTheme + +@Composable +fun SpotifySearchScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onQuery: (String) -> Unit, + onClear: () -> Unit, + onToggleScope: (SearchScope) -> Unit, + onTrack: (SpotifyTrack) -> Unit, + onArtist: (SpotifyArtist) -> Unit, + onAlbum: (SpotifyAlbum) -> Unit, + onPlaylist: (SpotifyPlaylist) -> Unit, + onShow: (SpotifyShow) -> Unit, + onDismissNotice: () -> Unit +) { + var expandedTracks by remember(state.query) { mutableStateOf(false) } + val results = MockSpotifyDataSource.search(state.query, state.searchScopes) + val actions = withNotice( + state.notice, + onDismissNotice, + buildList { + add(spotifyBack("Back", onBack).first()) + if (state.query.isNotEmpty()) add(SideAction("clear", "Clear", SidekitIcon.Close, tone = ActionTone.Muted, onClick = onClear)) + } + ) + val scopes = SearchScope.entries.map { scope -> + SideAction( + "scope.${scope.name}", + scope.label, + tone = if (scope in state.searchScopes) ActionTone.Primary else ActionTone.Muted, + onClick = { onToggleScope(scope) } + ) + } + val focus = remember { FocusRequester() } + SidekitScaffold( + title = null, + header = { + Box( + Modifier.fillMaxWidth().height(SidekitTheme.spacing.headerHeight) + .padding(horizontal = SidekitTheme.spacing.itemPadding).padding(top = SidekitTheme.spacing.headerPaddingTop) + ) { + SidekitTextField( + state.query, + onQuery, + Modifier.fillMaxWidth().semantics { contentDescription = "Search Spotify" }, + textStyle = SidekitTheme.typography.title, + placeholder = "Search Spotify", + focusRequester = focus, + focusOnKeypad = true, + singleLine = true, + autoFocus = true + ) + } + }, + actions = actions, + options = scopes + ) { padding -> + val listState = rememberLazyListState() + SidekitList(padding, state = listState) { + when { + state.query.isBlank() -> item(key = "idle") { + EmptyMessage("Search your library and Spotify", "Tracks, artists, albums, playlists and shows") + } + + state.query.trim().length < 2 -> item(key = "short") { + EmptyMessage("Keep typing", "Type at least two letters to search") + } + + results.isEmpty -> item(key = "noResults") { + EmptyMessage("No results for \"${state.query.trim()}\"", "Try another spelling or turn on more scopes in Options") + } + + else -> { + var slot = 0 + if (results.tracks.isNotEmpty()) { + item(key = "tracksLabel") { SectionLabel("Tracks") } + val shown = if (expandedTracks) results.tracks else results.tracks.take(5) + itemsIndexed(shown, key = { _, track -> "t${track.id}" }) { index, track -> + SpotifyMediaRow(track, Modifier.listFade(listState, slot + index), secondary = track.artist) { onTrack(track) } + } + slot += shown.size + if (!expandedTracks && results.tracks.size > 5) { + item(key = "moreTracks") { TerminalRow("More tracks", secondary = "${results.tracks.size - 5} more", onClick = { expandedTracks = true }) } + } + } + if (results.artists.isNotEmpty()) { + item(key = "artistsLabel") { SectionLabel("Artists") } + itemsIndexed(results.artists, key = { _, artist -> "r${artist.id}" }) { index, artist -> + SpotifyLibraryRow(artist.artRes, artist.name, "${artist.albumIds.size} albums", Modifier.listFade(listState, slot + index)) { onArtist(artist) } + } + slot += results.artists.size + } + if (results.albums.isNotEmpty()) { + item(key = "albumsLabel") { SectionLabel("Albums") } + itemsIndexed(results.albums, key = { _, album -> "a${album.id}" }) { index, album -> + SpotifyLibraryRow(album.artRes, album.title, "${album.artist} · ${album.year}", Modifier.listFade(listState, slot + index)) { onAlbum(album) } + } + slot += results.albums.size + } + if (results.playlists.isNotEmpty()) { + item(key = "playlistsLabel") { SectionLabel("Playlists") } + itemsIndexed(results.playlists, key = { _, playlist -> "p${playlist.id}" }) { index, playlist -> + SpotifyLibraryRow(playlist.artRes, playlist.name, "${playlist.owner} · ${playlist.tracks.size} tracks", Modifier.listFade(listState, slot + index)) { onPlaylist(playlist) } + } + slot += results.playlists.size + } + if (results.shows.isNotEmpty()) { + item(key = "showsLabel") { SectionLabel("Shows") } + itemsIndexed(results.shows, key = { _, show -> "s${show.id}" }) { index, show -> + SpotifyLibraryRow(show.artRes, show.name, show.publisher, Modifier.listFade(listState, slot + index)) { onShow(show) } + } + } + } + } + } + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt new file mode 100644 index 00000000..adaaabd9 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -0,0 +1,220 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.design.action.ActionMotion +import hrln.interactive.sidekit.design.action.ActionTone +import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.components.ItemColour +import hrln.interactive.sidekit.design.components.SidekitCard +import hrln.interactive.sidekit.design.components.SidekitItem +import hrln.interactive.sidekit.design.components.SidekitList +import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitSliderItem +import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.icons.SidekitIcon +import hrln.interactive.sidekit.design.theme.SidekitTheme +import kotlin.math.roundToInt + +private enum class SettingsConfirmation { SignOut, ClearHistory } + +@Composable +fun SpotifySettingsScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onAudioQuality: () -> Unit, + onVolume: (Float) -> Unit, + onClearCache: () -> Unit, + onClearHistory: () -> Unit, + onSignOut: () -> Unit, + onNotice: (String) -> Unit, + onDismissNotice: () -> Unit +) { + var confirmation by remember { mutableStateOf(null) } + val back = spotifyBack("Back", onBack).first() + val normal = withNotice( + state.notice, + onDismissNotice, + buildList { + add(back) + if (state.signedIn) { + add(SideAction.delete("signOut", "Sign out", onClick = { confirmation = SettingsConfirmation.SignOut })) + } + } + ) + val actions = when (confirmation) { + SettingsConfirmation.SignOut -> listOf( + SideAction.cancel("cancelSignOut", "Cancel") { confirmation = null }, + SideAction.delete("confirmSignOut", "Sign out", onClick = { + confirmation = null + onSignOut() + }) + ) + + SettingsConfirmation.ClearHistory -> listOf( + SideAction.cancel("cancelClear", "Cancel") { confirmation = null }, + SideAction.delete("confirmClear", "Clear", onClick = { + confirmation = null + onClearHistory() + }) + ) + + null -> normal + } + SidekitScaffold( + title = "Settings", + actions = actions, + options = listOf( + SideAction("about", "About", SidekitIcon.Docs, onClick = { onNotice("Sidetracks 0.1 · a Sidekit UI mockup") }), + SideAction("cache", "Clear cache", SidekitIcon.DeleteForever, onClick = onClearCache) + ) + ) { padding -> + val listState = rememberLazyListState() + val first = remember { FocusRequester() } + LaunchedEffect(Unit) { runCatching { first.requestFocus() } } + SidekitList(padding, state = listState) { + item(key = "quality") { + SidekitItem( + "Audio quality", + Modifier.listFade(listState, 0).focusRequester(first), + secondary = state.audioQuality.label, + secondaryToEnd = true, + showLinkArrow = true, + onClick = onAudioQuality + ) + } + item(key = "volume") { + SidekitSliderItem( + SidekitIcon.MusicNote2, + "Volume ${(state.volume * 100).roundToInt()}%", + state.volume, + onVolume, + Modifier.listFade(listState, 1) + ) + } + item(key = "streaming") { + SidekitCard( + Modifier.listFade(listState, 2).fillMaxWidth(), + color = SidekitTheme.colors.surfaceA2 + ) { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + SidekitText("Streaming only", style = SidekitTheme.typography.bodySmall) + SidekitText( + "Offline downloads are not available in this build", + style = SidekitTheme.typography.caption, + color = SidekitTheme.colors.surfaceA6, + maxLines = 2 + ) + } + } + } + item(key = "cache") { + SidekitItem( + "Artwork cache", + Modifier.listFade(listState, 3), + secondary = "12 MB", + secondaryToEnd = true, + showLinkArrow = true, + onClick = onClearCache + ) + } + item(key = "history") { + SidekitItem( + "Clear history", + Modifier.listFade(listState, 4), + secondary = "${state.history.size} tracks", + secondaryToEnd = true, + showLinkArrow = true, + onClick = { confirmation = SettingsConfirmation.ClearHistory } + ) + } + item(key = "account") { + SidekitItem( + "Account", + Modifier.listFade(listState, 5), + secondary = if (state.signedIn) "Signed in as You" else "Not signed in", + secondaryToEnd = true + ) + } + item(key = "signout") { + SidekitItem( + "Sign out", + Modifier.listFade(listState, 6), + colour = ItemColour.Destructive, + onClick = { confirmation = SettingsConfirmation.SignOut } + ) + } + } + } +} + +@Composable +fun SpotifySignInScreen( + state: SpotifyUiState, + onBack: () -> Unit, + onLogIn: () -> Unit, + onCopyLink: () -> Unit, + onDismissNotice: () -> Unit +) { + val colors = SidekitTheme.colors + val actions = withNotice( + state.notice, + onDismissNotice, + listOf(backToLauncher(onBack), SideAction.forward("login", "Log in", onLogIn)) + ) + SidekitScaffold( + title = "Sign in", + actions = actions, + options = listOf(SideAction("copy", "Copy link", SidekitIcon.Docs, onClick = onCopyLink)) + ) { padding -> + val listState = rememberLazyListState() + SidekitList(padding, state = listState) { + item(key = "explain") { + SidekitCard(Modifier.listFade(listState, 0).fillMaxWidth()) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + SidekitText("Sidetracks plays from your Spotify account.", style = SidekitTheme.typography.bodySmall, maxLines = 2) + SidekitText( + "Your password is entered in the browser, never here.", + style = SidekitTheme.typography.caption, + color = colors.surfaceA6, + maxLines = 2 + ) + } + } + } + item(key = "login") { + SidekitItem( + "Log in with Spotify", + Modifier.listFade(listState, 1), + showLinkArrow = true, + onClick = onLogIn + ) + } + item(key = "status") { + SidekitItem( + "Status", + Modifier.listFade(listState, 2), + secondary = if (state.signedIn) "Connected" else "Waiting", + secondaryToEnd = true, + colour = ItemColour.Muted + ) + } + } + } +} + +private fun backToLauncher(onBack: () -> Unit) = SideAction.back("back", "Launcher", onBack) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt new file mode 100644 index 00000000..fb911bbe --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -0,0 +1,301 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +data class SpotifyUiState( + val signedIn: Boolean = true, + val current: SpotifyMediaItem? = null, + val upcoming: List = emptyList(), + val history: List = emptyList(), + val isPlaying: Boolean = false, + val positionMs: Long = 0L, + val shuffle: Boolean = false, + val repeat: RepeatMode = RepeatMode.Off, + val savedTrackIds: Set = MockSpotifyDataSource.liked.map { it.id }.toSet(), + val savedCollectionKeys: Set = setOf("playlist:late_night", "album:night_signals"), + val followedArtistIds: Set = setOf("aurora", "mira"), + val filter: LibraryFilter = LibraryFilter.LikedSongs, + val searchScopes: Set = SearchScope.entries.toSet(), + val query: String = "", + val audioQuality: AudioQuality = AudioQuality.High, + val volume: Float = 0.72f, + val notice: String? = null +) { + val savedCurrent: Boolean get() = current?.let { it.id in savedTrackIds } ?: false +} + +/** + * One state holder for the mockup. It owns the queue, a reasonable simulation of playback and + * every optimistic save/follow, so navigation never resets the visible account. + */ +class SpotifyViewModel : ViewModel() { + private val _state = MutableStateFlow(SpotifyUiState()) + val state: StateFlow = _state.asStateFlow() + + private var context: List = emptyList() + private var contextIndex = 0 + + init { + viewModelScope.launch { + while (true) { + delay(1_000L) + tick() + } + } + } + + fun dismissNotice() = _state.update { it.copy(notice = null) } + + fun notice(text: String) = _state.update { it.copy(notice = text) } + + fun play(items: List, index: Int) { + if (index !in items.indices) return + val item = items[index] + if (!item.playable) { + notice("Not available on this account") + return + } + val previous = _state.value.current + context = items + contextIndex = index + _state.update { + it.copy( + current = item, + upcoming = items.drop(index + 1), + history = if (previous == null || previous.id == item.id) it.history else (listOf(previous) + it.history).take(HISTORY_LIMIT), + isPlaying = true, + positionMs = 0L, + notice = null + ) + } + } + + fun playItem(item: SpotifyMediaItem) = play(listOf(item), 0) + + fun playCollection(collection: SpotifyCollection, index: Int) = play(collection.items, index) + + fun playArtist(artist: SpotifyArtist) = play(MockSpotifyDataSource.topTracks(artist), 0) + + fun playLibraryTrack(track: SpotifyTrack) { + val recent = MockSpotifyDataSource.recentlyPlayed + val at = recent.indexOfFirst { it.id == track.id }.takeIf { it >= 0 } ?: 0 + play(recent, at) + } + + fun togglePlayPause() = _state.update { + if (it.current == null) it else it.copy(isPlaying = !it.isPlaying) + } + + fun next() = advance(auto = false) + + fun previous() { + val s = _state.value + if (s.current == null) return + if (s.positionMs > 3_000L) { + _state.update { it.copy(positionMs = 0L) } + return + } + val previous = s.history.firstOrNull() + if (previous == null) { + _state.update { it.copy(positionMs = 0L) } + return + } + _state.update { + it.copy( + current = previous, + upcoming = listOfNotNull(s.current) + it.upcoming, + history = it.history.drop(1), + positionMs = 0L, + isPlaying = previous.playable + ) + } + } + + fun seekTo(positionMs: Long) = _state.update { + val duration = it.current?.durationMs ?: 0L + it.copy(positionMs = positionMs.coerceIn(0L, duration)) + } + + fun toggleShuffle() = _state.update { + val on = !it.shuffle + it.copy(shuffle = on, upcoming = if (on) it.upcoming.shuffled() else it.upcoming) + } + + fun cycleRepeat() = _state.update { it.copy(repeat = it.repeat.next()) } + + fun toggleSaveCurrent() { + val item = _state.value.current ?: return + _state.update { + val saved = it.savedTrackIds.toMutableSet() + if (!saved.add(item.id)) saved.remove(item.id) + it.copy(savedTrackIds = saved, notice = if (item.id in saved) "Saved to Liked Songs" else "Removed from Liked Songs") + } + } + + fun toggleSaved(track: SpotifyTrack) = _state.update { + val saved = it.savedTrackIds.toMutableSet() + if (!saved.add(track.id)) saved.remove(track.id) + it.copy(savedTrackIds = saved) + } + + fun savedCollection(collection: SpotifyCollection): Boolean = collectionKey(collection) in _state.value.savedCollectionKeys + + fun toggleSaved(collection: SpotifyCollection) = _state.update { + val keys = it.savedCollectionKeys.toMutableSet() + val key = collectionKey(collection) + val wasSaved = keys.contains(key) + if (!keys.add(key)) keys.remove(key) + it.copy( + savedCollectionKeys = keys, + notice = when (collection.type) { + SpotifyCollectionType.Album -> if (wasSaved) "Removed from Library" else "Saved to Library" + SpotifyCollectionType.Playlist -> if (wasSaved) "Removed from Library" else "Saved to Library" + SpotifyCollectionType.Show -> if (wasSaved) "Removed from Library" else "Saved to Library" + } + ) + } + + fun followed(artist: SpotifyArtist): Boolean = artist.id in _state.value.followedArtistIds + + fun toggleFollow(artist: SpotifyArtist) = _state.update { + val ids = it.followedArtistIds.toMutableSet() + val wasFollowing = !ids.add(artist.id) + if (wasFollowing) ids.remove(artist.id) + it.copy(followedArtistIds = ids, notice = if (wasFollowing) "Unfollowed ${artist.name}" else "Following ${artist.name}") + } + + fun setFilter(filter: LibraryFilter) = _state.update { it.copy(filter = filter) } + + fun setQuery(query: String) = _state.update { it.copy(query = query) } + + fun clearQuery() = _state.update { it.copy(query = "") } + + fun toggleScope(scope: SearchScope) = _state.update { + val scopes = if (scope in it.searchScopes) it.searchScopes - scope else it.searchScopes + scope + it.copy(searchScopes = scopes) + } + + fun cycleAudioQuality() = _state.update { it.copy(audioQuality = it.audioQuality.next()) } + + fun setVolume(value: Float) = _state.update { it.copy(volume = value.coerceIn(0f, 1f)) } + + fun clearHistory() = _state.update { + it.copy(history = emptyList(), notice = "Listening history cleared") + } + + fun clearCache() = _state.update { it.copy(notice = "Artwork cache cleared") } + + fun addCurrentToPlaylist() = _state.update { + it.copy(notice = "Added to Late Night Drive") + } + + fun moveToNext(item: SpotifyMediaItem) = _state.update { + if (item.id == it.current?.id) it else it.copy(upcoming = listOf(item) + it.upcoming.filterNot { queued -> queued.id == item.id }) + } + + fun removeUpcoming(item: SpotifyMediaItem) = _state.update { + it.copy(upcoming = it.upcoming.filterNot { queued -> queued.id == item.id }, notice = "Removed from queue") + } + + fun addToQueue(item: SpotifyMediaItem) = _state.update { + it.copy(upcoming = it.upcoming + item, notice = "Added to queue") + } + + fun clearUpcoming(): List { + val removed = _state.value.upcoming + _state.update { it.copy(upcoming = emptyList()) } + return removed + } + + fun restoreUpcoming(items: List) = _state.update { it.copy(upcoming = items + it.upcoming) } + + fun jumpTo(id: String) { + val s = _state.value + if (s.current?.id == id) { + _state.update { it.copy(positionMs = 0L, isPlaying = true) } + return + } + val at = s.upcoming.indexOfFirst { it.id == id } + if (at >= 0) { + val skipped = s.upcoming.take(at) + val target = s.upcoming[at] + _state.update { + it.copy( + current = target, + upcoming = it.upcoming.drop(at + 1), + history = (listOfNotNull(it.current) + skipped).take(HISTORY_LIMIT), + positionMs = 0L, + isPlaying = target.playable + ) + } + return + } + s.history.firstOrNull { it.id == id }?.let { playItem(it) } + } + + fun signOut() { + _state.update { + SpotifyUiState( + signedIn = false, + savedTrackIds = emptySet(), + savedCollectionKeys = emptySet(), + followedArtistIds = emptySet() + ) + } + } + + fun signIn() = _state.update { SpotifyUiState(signedIn = true) } + + private fun tick() { + val s = _state.value + val current = s.current ?: return + if (!s.isPlaying) return + val nextPosition = s.positionMs + 1_000L + if (nextPosition < current.durationMs) { + _state.update { it.copy(positionMs = nextPosition) } + } else { + when (s.repeat) { + RepeatMode.Track -> _state.update { it.copy(positionMs = 0L) } + RepeatMode.Context -> if (context.isNotEmpty()) play(context, 0) else advance(auto = true) + RepeatMode.Off -> advance(auto = true) + } + } + } + + private fun advance(auto: Boolean) { + val s = _state.value + val current = s.current ?: return + val next = s.upcoming.firstOrNull() + if (next != null) { + _state.update { + it.copy( + current = next, + upcoming = it.upcoming.drop(1), + history = (listOf(current) + it.history).take(HISTORY_LIMIT), + positionMs = 0L, + isPlaying = next.playable + ) + } + return + } + if (!auto) return + when (s.repeat) { + RepeatMode.Context -> if (context.isNotEmpty()) play(context, 0) + RepeatMode.Track -> _state.update { it.copy(positionMs = 0L) } + RepeatMode.Off -> _state.update { it.copy(isPlaying = false, positionMs = 0L) } + } + } + + private fun collectionKey(collection: SpotifyCollection) = "${collection.type.route}:${collection.id}" + + private companion object { + const val HISTORY_LIMIT = 20 + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt index e7abef14..c81c4877 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt @@ -32,6 +32,7 @@ import hrln.interactive.sidekit.apps.sideport.SideportLinks import hrln.interactive.sidekit.apps.sideport.openPorted import hrln.interactive.sidekit.apps.sideport.sideportGraph import hrln.interactive.sidekit.apps.sideticks.sideticksGraph +import hrln.interactive.sidekit.apps.spotify.sidetracksGraph import hrln.interactive.sidekit.apps.sideweather.sideweatherGraph import hrln.interactive.sidekit.apps.translate.translateGraph import hrln.interactive.sidekit.apps.tripplanning.tripPlanningGraph @@ -84,6 +85,7 @@ fun SidekitNavHost(navController: NavHostController = rememberNavController(), h translateGraph(navController) calculatorGraph(navController) musicGraph(navController) + sidetracksGraph(navController) cameraGraph(navController) sideweatherGraph(navController) // Sidephone everyday apps (96:419) diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt index 003d789d..5b2638f0 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt @@ -21,6 +21,7 @@ enum class SubApp(val label: String, val icon: SidekitIcon, val designed: Boolea Files("Files", SidekitIcon.FolderCopy), Maps("Maps", SidekitIcon.Map), Music("Music", SidekitIcon.MusicNote2), + Sidetracks("Sidetracks", SidekitIcon.MusicNote2), Clock("Clock", SidekitIcon.Alarm), Video("Video", SidekitIcon.SmartDisplay), Vault("Vault", SidekitIcon.Encrypted), diff --git a/app/src/main/res/drawable-nodpi/cover_aurora_vale.png b/app/src/main/res/drawable-nodpi/cover_aurora_vale.png new file mode 100644 index 0000000000000000000000000000000000000000..7455fc71abc3a2894885b77c52e324231637e37c GIT binary patch literal 16112 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 BsAK>D literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_coastline.png b/app/src/main/res/drawable-nodpi/cover_coastline.png new file mode 100644 index 0000000000000000000000000000000000000000..a208893a05a45a250262ecf5b411e2ca946a408e GIT binary patch literal 21560 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 Tj;WmI(*d_{>S~m!A%gxNmnk=n literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_glass_hours.png b/app/src/main/res/drawable-nodpi/cover_glass_hours.png new file mode 100644 index 0000000000000000000000000000000000000000..0ba8ff267a7773917732d97d78d596ed2f47bd07 GIT binary patch literal 21729 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

3e3pJ{5lBCx->Ed6N1x3;*ble0FR>?5(m! zvkz&42wV*CBKNcJs-c7NbRm8&a-{DQAp_a}^gygO56yj$pm9}%j32!A5x;-`_CR^a zOa0GHwUbqvxKVHJcT&MC#XGLjiUXRg?)`zLI^RK_XxE^f-hwxQiPeAf5z8n&4MJQ* zh55a2Tl+@c51FeYuf1B&^w@B76)4SgG&Cx1s$Jno>(%jCikFb!bXK6`mp-m{ICRA)UcG@++-8OoS+0lE z?-!gw!f<;2MkFAL+e~ngFp!7R%TB8$XNQ*r|IbxiEA@i+N1#l`+DE?nO#SC&V+zM) zT2cLfo+`Rx4oGkOBpOe+`0jZ1SY<+3=CB+gcB#U5{9Vk7(t~=`W1);4d1?iLg`HOz zX@sCf`@QkkUH%IFtrid~rbq@$-N5nKhddIWK57Pq@ zRWc&h^e)J?1~kQbD}$`SG$ti7GTeG&6#2EF>S-v&w_NT77oYlCNpl0PF3Z-z*|Eik zyNj=5W?cnSi_tw*U0fTv`?W?z;Rm{eYkud9WWahp-i4+eI4!|LCs0Hav=;rLu$Hsh z*^%I5qzh^k%Gs1uQ6iBWomu<$4<)0kKJr()Q2*3`&RRSfda(R@Zra6Ool#%mtvQ)w zZaEH|;D^0n`m)y;0d^#^3H?<5=dEBDl&_f_dj?;~&9K+9qYFa8E*MIyf*sp8G!%>0 zhEF6DKB9lsFaioDR%59o2f9dUSCsFrT=el;JJ{mMfBs-Ok^tt#as3!{x#~~10-Shg z4bEmbqL4m!kd-?9aiYjWhN17S zh^3o!{vqA85=yliww>)sh85{S?)-?s+x(YuY{t|TadBl?o(hkMMv8-%9EQsYf=R5s z->hInyR1r+Z%Pt9H9~?;=WTak3Jrk4jz4}R0!+d^?8SEF9GOUpm&8~<5W1F#5$G+ zuvSJ*c+eseSkcAaqqo`V8wjRrMeG@D9IUeDy+l&>mWPX8Mib+H!h>M`F<$X}B5x9t{} z`CH4=l$~te)BcJ3z>(h`Kp4!hh90TFK@kdPGqJast@aP-Dz0zKJzN=r+qYaySKzmC zD@9hOxK-zR+))e8LggT~d*EVF_tJ~xZKv>j?Cp^_%5?f z@btbVpD^!Y8`XEhZ~Tbwf@D zI#MlL3w5U#`0V)c5FgwE0{OTvvmYYdG(TKL__UbujVE@m%~hFE2fj zbNFYLA^g{xB?CbbU;YY_MzSl%f7dK5dy}*D6DqTr5m|h)xOMyL9k{+x%?iOH`$#MP zhxJXgQzcs1lFsC>4z>&3fPFiiAl|${0miSq%Npm!=dN8W5j2mdhikd;@XX8h#mz}G zbkm*O+CRop(aUuF>EsIMu6MwG_s7#TEeEUId)gdA7^Xe;ChOfilo~%+3THK*PGrB? z-xJ8HZrB=h?iYdc&s4;H2r(LO+aWfOoG;5}LtTWc_{rXvl+=BA(H=3M{1>+tbo}X_ zE6cTOKYDsNuC4nWP(sP#r4^BiJD+ljAFgF|6@|dQn^7q;%RBKY15F=o=Tjr* z2V0lkdkzm!x;2!W7zA|Y51EH_z_93+Cwoh;1^Vv}FNj#q?kEmKDocIxC*7wMARTEw zL&rL;HvcI!Y*C@DbvfT_3k{)vj>K(U=SA5SH&OX#s&cJ@ zVB~jPUub@!MqffW?27?uxfl~AdvW>a0wT9$Xj>sP|KOH6{BIU9s8GY~-v`#tJ~)U_ z-b#+7`OAjdHu|iWUtp_p&(#+;OmWOibeNg45ltT9PY2W90q*MA-Z2hyt7HZ zUanYEyGc*93;{N;z_zlxNV;9QQzf0i8tK}uRO3%MrmAQU7`3DT^_E^auQb|$>y)G1j0@j~9YoCafE8!y903|2LT&*`~lkU$1fWxI1Ge1O3{93NJLefm?7ZM#Vp&oZ)8dU=PkPkCEjl$oZIhGV|3*#;w1hUWbpE2Q_kM*FuVCot-~ zU=;u8{I(-taB{A}H-I7Td^y#JJ|lX8Qz0BX8Jr&=SGFel&?Ns1xcQ2TRULU#zf*ck z1gZoeVp#YO!?Y#gXvyTqB6B#Mm|ynRE@w&)Jn5drj_IhU#oGzuS~Zn9!N9x|S_uM+ z8P^eKfdKicJIrDLm#W;Tbsr7T4II57@cDD^D$<0=w%%q=yOqIw*FHq5>*l#Mt$4$5 zwf8|>ydnvR*xoyq;(hSAydq*3=`pZdnykJrl8Qit6ajJ2&WXR@v3E z^!Ys(C;ojGiXC2ZBCWSC2RZM69P=-7%e<;LG>8ss$rlxmdunP(Y_%=3ho|SB_A2(a z=m%`HwkD^N156gGrWd6Vq+?PSHDBl}(~^(<=L zkKO6-xllAUJpfZF8k6h^hv<8qXI+OQU_?~)hbky%B6blHapi*>{;sn1401(kcc*%c zE9^G*C+;a5gdV_>zQPb_jbcmPdQu`yzLi)cKOdTOk|D}2x<*ifqkE04|@9obv`F(zv4rYfMReBj+rVz4 zKK0W`#{O@<`^J{HS&Xj`96Wax8|f|s#$FN1cEiyEcPe}cPZc0U5IBQ;Vf}>f*Q#Ye zKB=ubWlzzy$rDFx-p-ZS`CHu2ii9_0X9j1FOfTW~%@^feuiVSfYsj7Rvs&4{CWa8h z$>wK*1GD-P*n?BKB3ws3b{2bPfquF72TiE+?ffER4WMjjP&^E)8cNsRmbbuTotalw zR;0O~)d)$|*}manFgtA7)Nu38Y|e;vcMRAR!m2>Y$TFM5PA~ik&0QDM2UM|#l(A-3 zr?^vpjYTT~{;YIk5u!71Bd)4r3%P;sjDY^t+gaItvmrHN?ZnX}0}p+<*lh4p*^7CQ zxTdbnX>|_w@48Ir`kp7%F%!7CWvkd{$?CO&FYaGV(H(ua^h5j4bI6Hpb)ej|q3K zUMFs0ny7czopnC&M_suTSF zFn4&-UCDjI>Po_UllrCIcl)}zE^|Y>iWPD1b;rN?*>0`MT?@v=Jh(42bu^Mi+)Eu} z&DZdCr1#yQF=nqH2snB(A~a{9Q}e-gxfNvx+t_RVK8ELe+s^KHvVDCI#x)9ahJzi= zE`!&y(Fq@d_ZJoxj+|RL<}Z`2%qPb~P-N6V?x33Wn!!~^pT3nG*MLi3zAx_H3-}Lw zf`t6&9}5U~jh3aK1n-fGSb|q&MH#x_Q>FJ}=mlDGDT9Oh+FoiX9)~<@PTpi*4D@{T z+6^2XeK>^9>f%RyWg5?%0gsFz96%At7^0PkXt(*_#}ih{)}@bO;=8+P%!+dUW3B?$!sm@e`K{FiPPasVi6KyFpZVA(rBfOW!o zY(4oU#^{AS02mdclg&wP5{++_ZP>JwCR8PnT8U!hrfcp5vA(5ID<#TIgB&$1W-Puw mM@^P!8)7=1e{tu@4uQ<+c~5ekPt6pu9H6GGtyHdH9rk}Z@*^0Q2$YOD$Ug0Iw;P zmn5VbXdSfmY!E zH;fL#LP}yl{s7=nCJ-*BiT)mWhv7ax!5$NUUm-FV9a;JxO;HmaM*2uLfjlqt&vgn+$X0Q{zx!Jg!~t4JJOS;8onJqf zFOI~H#UHMomPe6)mbwyxYz%JlzMXZt9?Qv;(w$MgyP`+Hrzo=JF!iq|;&@w+1Gaf3 zPfGjgO1=avMHFWt3d=_Qk6S&rxkdkGt~4LQ?Rlshb6m!yF#@hWi}|1AJK^D7KAgw; zddDG$15^X$G?J;GoX%~$GbBER_wFzO<7n&tSMzZTLh2`CAo`s4>kkJNGZ%KqhAh44 zM!YP(-&(>gIoFE_!VeQXW1p` zkS72fa$TpkA)ElgKyp4-qaKBIm6*~8~-0j*zHxSSzmn)}Wc5xM=- zvo4iR&~YSgtd0X~OLj2_$D|bQ3J-9oT2<1Vgv& z-^kj!`pxO{wx4&6#YgvtyqJ)g;u2 z83?6&+kYTUeIkqb;x0Vj1IOn*zSv#3eH;AC z?St2+soUo>?IJFF<)wo5i$5I&s%oEAlo~lNp z`rcMNcWm8-b4q#|$rrsmIYElSoB^`dE@s30FS}_R;k!;NzE{hBx0m-DzO#a8>5E5q z!}o|=tzEtI`u<(B+Q>dFEmJpF?lPS1qqz8`LW z?)%-|e6>4kO#AWhci^Jq4L+g@?s*J9Ho0Gwa=iIPQ7K|{>V=g4J;dd%+`R22J;F(D%2B?!oM$`y88ZlqL@62V5v;MRP`XE$iYBm!5L@YqO#CXL&%z;2$&<42(!A2pi6w%tk6{gfYL$_4DfM- zpzhTWvj5|V9OXc@6pRQHsVLtGP=JCo1Sn^V<_QL2Z6t~<_|=o!ZYyZ`TludbHV~-f z1}MPw^4P7$m}3`K`2 zVgP(%NVoKUaPD3Opdac!F_Iz40o2jE=gbJ0 z3Mw2BxPqVq+w7MZgyl%wdR&9K_(6a*Cz6X73NWG%%f(S76XQz+7&55&@S{Kjz@zDI zaF9}c0N@8D(sXD50~Ig~0XshtIfpm{z=#!6d89AXCQyx_gOr?nI6x0>)p@IMCFLvW zfkz?8-f7As`w#{Il=R3x;Db6I>4IO7Zk7JQhirP_1yLOd4tj2e3Mi@(^Hp9mRf89x zpXTX3kP%3E<@ocjAU!Y?8NH<4BF8cELV-Qr`H=Q&L*k$~bR1oI92*%!lpy1bgno~Z z2jrw>23bWMU{e1V>##okUvL34C8E?s20@9BlNEV^U<1f?*`HwtB{I-+;szPsNGlK` zr!6u}LUqDX)WEX6btDZyKI$SHA|Yorly7jrqyE^RJa407%o6RP9J# z69BL_=3MdE+phRW1&|(MxI;EoBrNN3qX&E*ki_rId|)WDBG7`i%aw8J0w)Y@_A=g~idspFqVt^eK z#<@Hs@6K#KOP~a4I6|AAU{~ax; zB0r92#vKv5t=l;YO>TxUwxumE-?CD}h-25KnoM<_=zppQi?s+F=kR?Wg6F)gukEH_ z_NGDM$ZLThwIWnfP17J-P!MgZy}qG{M-pH_R{+CFg1`1TRu`d)+to`EEF@(01nx9$ zQoMfE7q=ftg@v$TqXHql@2o%h9a9KbRavTgqOkXWwCMogm;9Zg+%LvwshD5^a1aOK zC<>#<9Y%{fCd8664)v#_U0m&yNp48scQB%rHTLdXZS6IhC3-GXPF(Oa(+>=od-P+P z0d{sSygK_UHFby%0vFH93#(URY*y%R6R1McV{wyVK_iWSI^v_Sj|Js;*E)OFLMe2W zEH%$L(|F!TYrpoPM51`jTqMn(j14qw*DqA#(*Y=vSB)2_$v);<)HU1K&SgBY-$Xln|$p-{YjH(wO$j3aN=_ys>w8Pt{ClwC-qw6JZXn3*gGhtb0)mw)}Sb7 z*r`F8QR{;H!4y~282My~GESAad=atP%JHlgdHJyQs$N7~_ zo8=uj(2(FCs~KIC8JrTt?lPWF`^=`=FrG^BTQ1Qr+$N7!h(0=@Je={~t>>Omp5GH? zdpnfREsghei=H+5y*!-bN~+o#xK_7RE$Tg-0us}yP|0^W|42DGp$<3NzW2-FuQ1Bu z31X8&ZF>o4+~>Eshk16@*cxh44GTOM0I$^-Rk2K_YDi7R47LWa8Do{EQElLQoe49L zMi@J>7Zttq*dp+f!jXYXI&#NSALu$I;{8J>2 zRps-tDM?8tKR!AP#xQ!aOXN?dx459ArPUZ2*@IV6hUu^<`tBV^#WTkwI35N~XWmB+ zNHB4opmBJP!q!%cVzRos2ci)=&|k6Kcs?GgZqWz6y8E89qbE^W6mG1`&K?R%Fv;^A zx<2m@WJlJXE0$;z+n47|8Ncp1g|Ypq+N$utP4yAyVx~+D`0z2!Z;%_5;MLmBvb~a= zOwJwfosJ~4=aR6=yV>_P;jou1-2#7VvR~gQ$!>9`)c!&wj0!qk+Fno_&Wa)jkBHFQ zZ_E#(BG{_pqr~(;*$1OC82&XjF8aBXK#8K+7^^C8;qbMfR+031$25PDzv$#un-oKr z5%i1L3SZ(0BgJvmd+kp1I?dqA$Tv!M6Dp>h+ttxwx_mFGjl@=}bpv_YPh~PTsca9Z zYl820v`fbPh0MJ$k@LV`G>GnKeir_Ug7@UhPTh{lW9wlEZqF{nO%?rFOAe4JrHESc z9z=Ug^YM?#rda@#e8R-V?Csm)J8$Iv0r%49n(EKH)G~Qjd9k^Cy}!OMO=`S)RmhIZ zXCd+(w!7lAUtT$?huW&FF1JM(ADg!>vzhbo+lH&wb~shs|fr;2~F zeJ~q^4t99QCas8x>~2ZRMmMu=X@9C>&ITL5G!Vpnfl9V3#t84gR57BAr)uz(l73@{ zdHfU;MB|Kc<6PPgzU6!gUy9QstM6Vl&;7?3bh+!b?M&QCVW#l{L6jaCHA#s7b=Yao z2|dU_#=loFfb61(k9e0SH@xM?Peqkdg)Ugw%zA#lGu$F!0Dl125c8d#-RmlDLD&U~ z@7O2lfjuIgXX0}3M8T)J&4<+3PUz!AZ4wi+%DR>U5SYk@|mNK&!@6$OF?}i zjNPKtA0ngGliKJLe3Vc1Air>Bh_>Z|skY zI5PT4C1`rjG+3kiE5Mib=`aXaOM0YDLQ2U5Ig}2FeT6T032U{aT8T!E6;0THvQmPw z%F*y4WgPGrZZz}>7d-jdP-hqcc%VtDsahzHySc}#e%pYYE^acu3nmm{EX38{&o8Lg zqSr9ix0>MpHZxeTxjfVL0qa$}_t&6u+*Nh$G+rg0auXdLn|spG5$KxfaY9=NJRJd| zJ0=j}c1gxmmhDzx?Z824+29joW9Rl^$4(DqB-r7;A$AFUiYt7XtmyTo!oVJ3sAI&w z2Q{iTtwg+Q3o$!cyFtOg5wmUm0lSvHSfc-@W-0tzC)z|=IjREky`qA)UegS^U>hyr zKwckW=FaQ|Yjcdj_!(%bbNRA5FmZBixfQXc5FPKYClFBR2SlbzkJiP0+fx;*YESX? zb5lv3I@?L^%LtZ2`DV|zNL-hc z;RbgXLYRIA{8g6kOP&|BoNW|eWhX3{@2Ga_&qwLh;JNZ%z~cILrmbTmE;*C&ndc`B z!y*}P(?2(*K~Ub+IulG>u4Wq5`p*6yJKNoItQ@noAbi7t$*p-MDVYTG^8iX`tnQ^Y zov&9pDub69=45AdW@Em$-rr6lB1wU-MrwMW|8);TNW(h37%BsET`qP>WUlpSs}2Td z6?A7YqcUsP7oiv@XoBdfT|xUv;^=b0ofGkj%IJho0|YxZ)KOGf+Nkyhr zKJex9p6Rn#@HdJqqJn)N_uqlCQd<05e)i9ZX%V-=mCO>?l2$3^BT3f?J^&%sgjnMDhlFQsyR?c7$bTZj?){0X}?TQ5}rx=NA`KB$K5-jQ+J<5WP;{ ztL0N{;}rlGU)Iu04Qrsbj2I?ilw&SOooo)u)p{|IIfY&c%s2ed-MF4pwT4GA2#%Yp zF1$IvHbbWzp@a_3)+Hd;sW4&B_H3RUs|6b2<~FDqS)zlI!<;dVrd8S!hOhIv0e{Yx zN)YL~pldwU4w<&KxWz=87h5l?)@e+Pr)HR0$OjsjnSI%}S_6Gd8>05^yQ;5C4ff09 zlCYgPzT@|+zhg+>RF-UKOvI0}OeMmWsab;LEq+K4d62w%tE7@?CBz#7{rPE<$F)aL zpMYGl=2ht1zuWWAH4nFlYMVg?B2pk;IqRR|@9l?i_gJ2U@DFZ+1~uF)OZGC9!*)n| z!Cy&WH)r-(ke8KLpa$n?rcew{Z-_wOLiCqkCY=L$uU{eY^JjuX-vu*&LK}TxhPgcC zQkI7l#d+R}qV}gJyx`yIKafyz)ksHHpwgwxNG?6F(yMym40I4JiWvk1($4 zQgOt&9P^C;7eXkgQr4;oDkR7vBmp`oLmD>L#FSpEkHc-OL}hDN!;;4gh8`;y3x0Cg zot{0G&7fjJc&h@O?M9RY__5`f_l={Q#j$wSDA)hOAaVcq$p^0FFtS68CKQ->zLCLN zwX!VdYih>E=4v_4zDO3PqvVs@rlC1!2^}(T23~DbQ#=@}&yBpiwh|N0)XE_yNB1jB z!>0xmefOQ`ci9uP$ zNO29}Bj*F~K%n_~{s28aoQB@K8n)H|VODqJw7tLqqQ zPN&9^I{nS_UK%5lh}#e~&_Dki#@t=!6T-gGfi|Jw?r!C;qUz(_(IV+&ozZ5jpVD~z z=Tl-f$ne8F8ts~~=8@%$aJM}ErSB72%J3`M9ky-(lCeh=c{V+EJ$w2-#0$!@cj3M( z$Iu_Aqd# zhlE4EObnJ~o8Sfg&C1x@j)*t}VVJg}vV_4TJw-xWK5!eFb?;*c?Q z5LI_N>eb;O~oicbr)XUVd~&uoxOe z%*W}PSuY}V`z_K`(g??t_f$LD-=EgmE@9=P4ct;GxngYMbniMC#Ub;LWCyenYx zOZnw~wly1b=Lc`|eIRbag>hfhdg{C>6kBL+8J_Kuchw=4TH;J#WBBP&!be z8LGF<3cNsB^&2#O7IQp+#zQH^VNCwr+e*j4*E+Wy&+X{WAY+HBoXWc)E62Y2u!E|u zzIv38zI6k?jEymAE-;<=&{QZ;7&WS|JAwV62nWY2FhhUO?9NM)whZZBFuK<^N zr(p?cu~xyPL6%4iTb>k4^jbCN-P!BA`6NTBp5D|iV=?EuxTZ1PnsFCB7r$h9$x}%y zWDehoOEd_-8P`{vQhIi3+R|wHkqF5pK>SXVcHjk}(7Fl-Ke=FW%STuqw6V=Kk<8fF z+Eh*3Ilad%F*>s71+J6{lV3AOlp~Hp!zeCSo3Lbn+7a&Rnu@CeJS1ND!g0oZdvv{u zYWlfnP(|&SrhyiJK#QezgpSsQW;A`eoEwINyS}7)P^MU14po8neW0rI-mK3@hWiX@ z!~*htPltwq`PV&B@TAt0 zhx~U!$HnUd|4x6D2LbT$_ONfukvUU}=bp{zU1#pl4`4j{AoR;-A>G-i>4U9&jh@h@ zupnkdn4Qaa^{77RPHeFCAqu2yq9w^Ri1%1mY`4D``;NkC+G7iM3M_Plan@~8^Q6054OIRz#JP3ph;bBlunU4S*+CH>Tm0jR)IKN^S{SftRw%DN6gTK{6^i@O-%e})6D$8Fnfv?DQ zo*?E4CGHCiLqq1t7P+si$r8EWK6OS!-qO+<6``BDKX-}h>(vuNzpx>ape0-#ASW*6 zVUX>(bq&{iYL_3NKiZp`b+ujiukqV|r7D&h2A%KgVUIwEg#(#P2_}-6bV!JFa zB}6(?)6&ey@K~sOU{(*~?XHo~OFOo`UcVf2ciBIzG`9dh%hRknbLAy_HzcgK(KMI^TRq{K zg=)z5I#T;V9x!ihLLGqdqyKP}nF>M>B011P*VXscAzVZd|uH$UKnxWerRuSzvF|MAKl}yM> zoP5cN8A^<*zjtD8*{i8^fEcLjzDuY2aZWor_k1%Ho}S@fqAUtwr-BGNCz!i3vFnWq zAFI5ZsjQ%$Ra`07eA;UBO#3`d^RM5;nggFnQPDxTGb>2VK9tSHi%2_a!1Q@Bj<$ia z#Gd@W@tN~rUiZ7U8p-_Z@^rX8I?n5mB$$-d@!{U@av3{CKLHZd=OWnG(W&fY~SE)t07JN{c0W z--B4dhtW0vB7x=7e2Hn{`imAdbL~KG4xSoy?ML`>IkYyeQr|OB8FkrQko4HIIf^67 zZ1#EWA5svncJ{6fZlxLdOI(R%$&Hf)66%)E-;rs)DA*z1bd+&&kNjHAGLQ#ZWOZ2j@bce!1;0-q)>!frldJO?asT({b1MT>BBgk#JBX_qI-p%dKi%Yb z;Lk)!sQMUJ<+a$Bx7kv0m}xNk)H<=ElJ~mqpZTcBme$!*W04=pzO>Y-*MFx$vB>`- zWl;%FS}T3WCk09=$3nq9%Q9y1@6XD9?3c0++NVO_13Gck2`1=*(rLwHMYjksfPiS~RH`)Cx z-Zrl_I)kB{K@8svKgGWlb%1K%`Grl2k9dQ{pO5`fwFzkokt>|52~QSqVNu!^vKGwG zLWN>WrYU7G*)`MUzvS|2SO!J)s&A?t~90EN;Z zTQI?bF&=Mz#Z8kb=}5UncrXdX3U6~!w`xDnJ4zUa^&4|HHdYsUIWUq z9)evkZ^(|SRqU6J@6qRe*o1Y_+XKG?Fd==ZyjjHZ?V=0B;+}1q%{1Yv362fI_IkYA zUL-`dmBQd;(KE5^d^UhRaWy&KrR9c`FuhrkBk4fiqFsf5VHTG!+ckEZ+4kGXW>nG3 z^NF7x?L`pH{cU+od3gi^@nw!NGbyF!%7&bbj7a8ym761Sn=3Ic2W+_hj=`$OMr!=3 zpY6g)Tuk8NI+7{>Iz%T0z+q+Hc=>i7V*K0OgmR9phpaBBnx6|5_d-!G#m zDVcDQvc}$4f#7cCXr(Q?bS)axmkcwi55h~e8M;#^GL22Du@|9lXWLn>ysx{f znlTs4i{nY5(X$9UJ-suUS74=mF^{m$tnm!Bt+R!jS7!4|(ZT3>z-P@U# z%u2iW3!aTR>fc5QJn86~Pr>NZu}R%j-bdrwRfYe>kcC^zu9s1$$28FdsiI(dP=g&k zLndel_$k*wJ~vk_IAi;J2Q?2d`qbDq)-Ykw?G7NUO zU02pO!uR|bj53HD(PW3pGm7?ARmj@etx8%bD1vT79!v9sdt?Y7Y71%kW|m)8QF=T@ z(8Q)P!Wm&HT6~%D6F2eee4$Fm(65)L= z23hC4&sRF>A6(gzfD6}4K`%6mg2-DslMUhzc~|CzA-Jm%*`5zPv|qh^ zyVqsTZ$p+AH9g ze+@_aZB3Zwa=yYjX43FXmwBqaG_|Rz|JaqMsKxB_O#C1)RjY7zVY2_V3ZWMa{-+!G zAsJ<>^zbQ+_`3VHrZNpK+_WKgjOgad@t;fTDZF1#*5)>=F42i1E^x!rUte%nPM1D> zawMhXNFGmSCagii&b_1_KS(Ge>P?Iz^0d76{p+7U>^IuO_Qj$EBY5^crM*Wh`c%jz zIAGcEz}$G!o~FG$Jz8IBnG#mB*-86_de%tbmkm+o7eT@C5{%dCD5&YuUkc)Alvfi{ zZ^Yb}4TO`FRG3!K(~Kr1X|N>H(u%ut#+p}`c+l2G7&X`JC2@cl*WYa&q6Er>Dj>qb zH`XCmzY2NS0&8~VP{vn3=T6P1qslrvZAi z0#jBJ$!Fjx#R%z?e-Z$05ve}|bAY0A$`tF!otQx_|` z%1D%3d?n-zZF0A=lBf7Z=T_xx;xo+26uXq?X1$Vv)d!|++OY4kx(cb}IIf2Wn_IA+ z>1fU~+wF*5%og~B`RfdEDorDJYN44p-gJT21O;DJ)zpd@X!y~VD1U^5n;OPOw2tkLk_3EM z4duW~Y)GNuQeYHZC2nov@fl=p_=$FbN-{ODLTKYX z3X8gvaN4Q;+canhp-j^cL9lFtMN&RVsJy)W83ecPY!4k(j@QROSmDVstx?z~+^ucxxTNnn#q2&B} zLe_Vgmxk1lTG#Yto+yHa5E)D@Y9CK+1v>kA!Z2~-RyI1Wo;=5;B!ky>1ElC)>0vGi zt10s{tI-$7OO9_>_J`bQ-@l8aWnrE`E*a~fV8{!L4pQ{u$~c#6`WswWCli5|gSx9V zCq9k_baM1AaEhevR~j9$ej(%^0O^~xNCt)fOIV{$aU^+CrnKfh@-*oNZEK~e(Y>ju zfO;m^ZMWHkS)7l3=5N4~06?^(FE@p{sF0;3i*Ej`%s*_GE7CT!1d!$^Eg6GYMIW>59U-PIlgj`75i;PguLO5TF9 zwx=`7{=-=Z%Xaqo^6%D47g_-rrENz7@1lWB#e7%HkMgJitS-YnsvUYZE{{Goy$?;(}HGC}FOvsgR&aLM-I_E#k2Klh`|^X(`=v zzNShFaxxzx^k_?YI))H8PxmOe^4eF7(v2tf0_@#NC^btfzuirPsIsO$VB_Fmpri8z zoRUvc2YFt4xHr=Ey&rhLK}mDa`%yI~EUZq(OrwOEe_PT_d~sQn-EX7GeV;>sjQGJ| zF^;hUwXfapozDlu+nVl}&iC{#W)O&_sVPtq!c33%r3mnS?(bk!;Z8q+Sl=+2xMH!5 zA=8ac=loIfubt6I+6J99Baa>2*V^9lt<;592pSqe8Psui5^*K(!NbL6xL7^YoOSK; zz&6A&i%;42;;qz}4P) z`a|U1T~10XhiN6{+XX@_#!XDQzT?;QrdvJzV|^i;2SuiWT(Yn{=BsbcKX&(9aUZ>gUFj^i{`| zs@{0Fu@YuoOtSol23g7F=n9H3LhouLwvObSKp;E4~1}Z%%rkugl&2%00 zxX9J@^EH=%sKo$sd+1~EEE;L>0y$OzVUN5#>lfGW-}#SN!Xu+6^UCbSv=wJ@n>t@f z-|#;xx;&nvzsYm)7X+`JK7)12h8K z?r7|W)rU~scHWM9Ou__%llvFQ5h)r>=HrTo?WzBC(dxjB{Mc_Vw?$U?_cLgjJ!A)R zs!j|$UwG9KwlS!Y z`g?9fIk#eDoy$o1qN=#I+{cw3rw~#cy}K4)#V1}*?(rGttG7+HRD^bE5*F8pNoPb; zQ^dI?V1WPQoui=wW;(l`(6e?~S3N7!4_gDTZrD%HSOgIFQgo$}st*H3)aDzI z_ddYRmrL9cO@b1s7uAMY7872U9j1z?K!6~CZu7-A)>2lVG8s^Ut+c48UCyA>n>b1q zSE}izCRdF`xG;Wirx$?zlai_m`S^a9Y3W~%zRiMmREk)Y|tPHEoF@Jj)(wNA%0I@7<&_&}GxSsb*l~yL!$3`g78;u9g45i~0oItedygHQ^2GLaPyq|b%}XAW%5tUl zcWJ^2(an%xFmrewy%L9@o7DRjbP_DI)y&uA{|7QP3UqI-5 zdzH+L83QhAl?DIWGbpa}){AXDnKb3DKMhuKr(+D}vBUMowoK8O(K~YRr5J7WcE7gf zvZ-EOi;Nl!4D=D&O#SEmqr)GGDLf$8vZ`gZQnLhZ`d$aG@p1jftiSG;0{%7^o|L(R`~aDamF-$-0{SN8$;_FVUdrb8k=wNGk>U)0F)I1O$#d!qJ>kE1jxj;b zBt%@_DlCN zmB|Aw6!ZV0X1nOg7iH$aOYb5ia8kqiSDQnK4Nl#0t&xH-3tL=rrL=<&Vjl+B%quY( zM9B4UCiXL$mg-O!PevL1h}$PM#0|w+p4haaG2m847^AM=n;2D`cwqJP**X#?xy4zw zcUW>8s;+s|cp6wauygqEp*{5@u5d#ALEi8-BNw%izizKY(H810Z*aov+tgf6k1OQb zhyOXi_24Px1lIm>=ed$s z_S}yJ{&Dw7NT<93!w~KeXBb6{Y#yp<0L6)9f+t9ybPQp?&-fvT&mNW)S~q5VF{Jq? z6f#m6t&B z56rKH(HYdPq(dmu=wJvk@0_=jHbGwNi@IaX_`@%SG^iPGj0xgXecWCRX9=$n5|^zl zAP*I{b{&^VblyjvwCRu8crDV^abm#=;^m@nKLjxdcYGU!EA+T9juuSUJTe29M?A=bo}biXHkSt& zEX9OdnI_nkrJT)Pf8Ft&SY-04zWNm`!8vJnCNJAGB2tKbP}Osb(f5OYWKNDR0SnUS zDJhX;yT@jpjG>&1WZv8FJn3|LD)7tt_sq8zcCODMikYdm_{L|V0$>V@6mQq`)D!Tf zE?2h*UZ=ztH0E9EJTYaGGE^<`4bbMh?c3&TZT}T1j@)*JE7}$hSBPLdO;SA>cgaY+ z)^nyg6TjE9?9s;`eLmCg6XcCB1akJN3FnF%C2$fSd(iBTy^cC>=3lYp4Hp2X4KI7S zT6To|XrGcdD#i*Koq|>rvnOR0tNVSF5dim!Bbz}W)U#X`sU+2DTEU6};DxSgh}jj1 z&^OF)H=LChiG%k`lTGvO#~I=&&w_SQXtz|zIc;5{Jgpff0DE(1{k|-+*fjZslikIj z#yeI4hT>cUQ2a$|gn2-Uyb(7MQI{yETN4A-03VI{~5Pr6!wP-VpCZ?6cx1GAGd# zDsigK5!gxRxqb-Y>Ewb(D{4iXHK2^0Jz#p7mH(yw&uT}<+QPTA6CibIdfhta>baB3 zig-#Dk7Jp%PBxWuySl!7Z&F;S8&owXy&DU3szKjzkutW>8DV2vaCC@D^ZJ3V=jC5z+2!3>BO0Ehi zXiZCkvIKmt0)5>v#d#Xw2Cryuh-z30)K75b2{b=dv*e%)$)s#~Fi@~}4f29T1S77; zv#to1#RAC$!Np*i8na)w?#1|y4vu-Ls7tTg8?Bv9Q}O1Qg>2uw-|&es5~?hLtz*_H zl3UObzJ%XvQ;&$1ElExDhEfcA>E9c-l}~g=QfnAP90<_-A~5~4+Y)&Q87-&%rfucp@|qtyiS?78@cG+!McnTUzDE;&7}a^ce3Qg` z=~FJ0@u``QqrX>Zgx{UF`T3TyjxE-ku|PI%`U5;C>V7)f zUf;SD8zbf4fY-u8XL{7S;M&X86ggoMuxgNZ;V}5CbvZnze+6IZbrM>+0q6~c)geY zJD%%M>~d~p%7O1W+*rbyc3i>z%BPfzAgbf7fpK|yeMxggWBG@L^?;`Nmg_qbPyd_S zfJQ{~_Edm(SNd$rzn(8;In7QF_lTAI-%;eZBr>eO#M^4$cO}1gO6wu_+UG9o@>N;p z8=l3bySpC!{v4l)c87O9M>)g(Vxq!ZQ!5Qq@Td7&7G0~mQ={U(zH!d@^w|qPzAjg# z4vu6=d}1Xfn(%5=bER5!c~ti+8zj84wDUd|Zs_ig7pe%uuBXy2UmsLvb0fdz z6z0W0_h`L3T2yV668w6J96SvH5B^*DNY`?(l{T#3Qjo}eE9Oe$^HVy$lkRT2X5|R6ijcZHcb#d6Usf68R%2eHOGqCGX#etbZvGbZ z^?WbU)Q70-5!J8`88)K72FJF%NGus%E;HE#d?s>BLfFVrK(MmbIYO!dcWsbAoby^V=UVL8kf zItK3twk2T|6~pQx`wIbFh?Kjr&&y}QNxTiQgf#&xZJo;)ZG%1O{hp4Fd;$IsCdX}c zHap#)XzLEL>5aV()upayn;$=nJwG}Q?OcftR`Re(%RgJslR4+ejVMPRa*(AWObHu8 zz6^p0*(J`KjCSq2%fUJ1cyWi?Z|CRJ3=F&jidc@VTXGJMt&5*8jY~4Wv(hS`*p_&f z_4nf;sB2YGxser5NsX`W-TN-@j?qybFQ{9&bdE1*;W+ktGf{9flum^EF~qE1PVBp%vK~$T{4q0ln<|Asxb5~u zGOVSkr#@|}T=(zpZTs{%oiBP8zs+cTSF`0;)W}3TN1-38dq0&-+m6`tl7i?wHv7nB6JwM?V19m_cDH#eT`?P-nj-+ol8{9|wm@%U>f9my zHq-UJ-=ap|9!(w-$q)yJsu_$JQ$=iuEncE|%D* z&5$a*d2^qeuBO$DO{NCW`)+bB8V?7v*let_anHsHCi}AiVvKlz%Lz_ruEhrYOroXP zHP2`k0Qv!*ro2egndB8{(`ST87(@=1JpYCkuWeP5QAOWBFh@^Ma4Nu{b%e<5EM^c= zK626ZIVwp2J+?1B?_;9*6(=6Bu%V^da2x+wc!g;XZD|hGdr4BcrB9j}I5LMQ71DLx zsj9DUai4#Q{LLzSzA{|MfGsVsWPmLV@wV^)_TluDAV35UILIVI2Bm!fA{lof4Ecga z2Pl<#!ZdFo15BjPFAbHC>?eQ{C^Y$a3=reT1Dx(Y9Rd>y0f>+=aBZ!{0n=tk7jr`F zW5@sz)VS@Xp{)G`$yNgOCLi!XBzPdWoxlMM2>%@Haxo_&^BFQADGd>3KS8#~lcNW^ z`a8h`;fHb4=W!SuQCvg{ZK=ocw1Er|!Fvl-4xx*$5=^Tei(!znxE~uHFuQ^8g_v zm014I;b|kQ8*KQ8R)70;PdWX}9ou_25l30M)9X(^u`KtTuNey&5Qh2N>HP!+cE6WZ zRE#Ys7#bXmx)Ol;MTEf2uUPUx3T_rCd~@&)_WNsc2ZTBQAp^3^plO|O(YT->x}_md zf6mDmdmczZ;W-Mg$?E|)Sl56cBLf0T;B3)&a4>R<#^C*&fuP9%N#KDL6h4*?c0C}b z&LA6Fnjr&9`w6JBFbmx6ciNL?P(r8 zG`66CmJLvUU~FKrp24F?FY|G#+L>vg<$)9w-oHiT4H4-PrdNi!-*@-`$hs<12?A!Z zI4v3nQyNM#?~5j^S?bX;Hx(m&?Q4nzZJ;?7p7}b%r5-13TQbHs^3xs~BsR3tXS|d% z-)qU-D$~?MLxXtb7ExR@wqB?*wqR&z&@!p^_Y;_Xz2JSx{5>$d4G;Ghhx+vNi3BI4 zpzz^o4~yp-yrCVhU{rl`+1LX9OYO%N3=Ivswp)^U-`DQGI7RxA7XbeVI{5a` zNM;7~>5r7q(n^3=FcQVmko~3hlr|{Z4ZI(=l5Z4YeE-@xu~B$k@~*g(7GS(%`o&TL z$bcMdX~>4LV>xRr;C(s@VWzN__)jm-g#Rp{@Ot{#vhr619KF%)o4lA+d!oB5AZgf` zof#N}95<2^yx7oAmpFDn39_+6S{hjJ54^Iy=UA$H&1rX^Vx+&( z^BV)ho&o$I1Q`I=!N-=Bn{L*XdD&e~3rv#BJANl^$Ci~p^L(onPXlweW!fPq&NiqrWKJjhAfOhfJsv}9&k;9pR zAP@!!A!84E!|C*i%^2neg1Q!6OJcNko%5tfmI`7^dOl^ zAgSp;lWTv5eW*twGsp`M1aRgLj)NX?@M+E++}g~)sNTOneWp?-VSW&5`~l*bCzo;E zWM*au^q{Va5c21TpGz#`D^8GO#YcK@`R>@Va@qyK5xsLB;C^Ly`)n9OFnQoW$rn6e z3c%Fqvs7wrj;>2LMH@T^x43qNi1(S#-O$<+>$RK+Z8U44#!rRa9haVWBEwm&+lG=x z_qhc1al!Z(&O3UVYoG5Pc|b5vT~1Z0>VOCGy#~OI^mS1`yO={q5I~4C1Fniw7dh?= zp(^C1K~pg^2=j(>E#x2%1kF2x2Z9rTjXYa#Y}L?SIc2)EJ+w9EZCkN~>9r}-XL5zy zoZHNZnjvM|7JuYnM!0+M|0byO;{lowh`7{aJ^-k|F98raSo?$0>>&EYjZpaL z%Yewmmhh{1c}5KmKn_$G0^gSVIZDs*;A$qj|!N=TQmluOZ~z?P?L+3%maBl49>C$wJ27&_=eq4e&suGXRygt-#h2!W{k}hIN`5aGh>lggwK* zsH?rZF9tl|t1zU3SNMvFOQtWm^lyK<@x6EU5JFD>%pGs<*;zb(=-`77{rkhem@wn` zx)UxTgp^gy+}_?y2wD5WgQs2g??3;y69^%tWfM<0`%9xnr}pjL`{bX#e{}gI`j3w) zDA?Ti)Xt7Z;!pdUuIq~Jcl6vq80&pM0ZKy&S=4n=(g`yU_!=Olf>-#`iWwd2pDih$ z*0W{B#@6Oj-hc1*|2UrzQhZGHnOFbd_dmL1OUv_wkgxyjpMSdT8uOo=cGh1hC!TxVBmeuY)c)59AxBsIs_D7^KDMlUr<1Pz?_0mBoH)OGYlHm$+nOH#^bL2tG1U3q-e(7PZjNy{yiwtd z7nC^k?k7O0E&Mxmle|wzF+g;*KPF{(?z5RZEr-Hy8tK*Dw7&WD%m1@v+=NN9PCV?0 zqKcZ?otx`Nr^bBhy5Ei(ohm3z-}%D}>F2Q&e$?pE=9Qi;D|dA@Po90!w1pS#>T24& zcA0tGQ}*uNw7z-M`eq}Jr+)nXuMQ#tYvNgE2bgiF7)il^Uf6(ZWYeUM=Bb@ta{4!} zJiz_3IZH;?kmio(lowA+6rTHG#;8$ai!1*3mP-jCm6H}!PhH%(xqj~l@8A1h*APPH zpLE6a`DeVoYxo~pq;KKi4;}X5BZ`N1w-4-Se(cY;fA%}~FTLygNuF$$FmN>(M5ZFB z5`JNR1v859@l%EG>~(EDMc#)~B;%RkXY*H##Zo>gJon#@tDMpI%2xB|?d?q`oqxSa z^yWQ{y{}D}ar&BFLXU2KdHrX;`R99ne)XpH%>^atJwv_lJ#BW{_jbo)&UlQZEHlD@ zz7n*fiUJ(rbUa&uyZM(Xm3T8ZOJn@>ZhOk!z2*;3-FNdjpZW0z@4vTi-@YgB{XzD}8Hx9Oi$fx7!Gy_< zq#mcNG$U6e)snnO{|uEySIaV+);oC7llG&NZvJAOfF@?BoN+@yG0A9Rd(!=`nA5JCVvz&j4w;3@ou78ed-hSbg- zJ;#)l@7$5C@bGh9Vu1}=M0U)$@|`<+EMH8XJ_9n4(|UxEW6H$GQ&ZPXdzyCs$5SE7 z3{Lvw8P@B=;H?;J~jdRt~kK6j_1bxN;CeSOkD}fEQ_IDlfk?x+JtAMDoiqtS0C*tgB_J&30FFvSSTjfz?}G>8 zA^?Vhrw#pm!9aBJkEaE-Y+hfy*#(bw(il*`a8VaL5M2R)!UJe01qk->BofB5-+eZx z!UKTEbwWF*V06J*Kn!;R0|0g!N4M02eLP7NfR}M78{iwB9ijM1($dh&;F-mXKQT;FQ|%px6gO=EH#z71sY=~_2W$%-AjvENeFY^T zAY7cqEe)C1EzJPbsh_bY1QkFD2nZc4V&ayD?Cb_2V=N;QPS^_YbQTZ*1c~RUFrQ}p z><#H;GLx`3LuKm!00?`|T7hz;?Z zFY&)BbZ-ybWrM$G0-SFnDl4_3&Jrj{U~|a&EE2s7 zC**6O51zy42tY9gpya?|33Q;)oy)H|I@gGb060xW1RIH!yOGRT=&9PDkf4s^*$1&i z5pJ==2d}i5$xJ}u@%Um+fB-Q73z3`-Q62o7TN(ri#?Amifr$w~g8*Ozz2=3!$Yx>z zYB~4;GzSR&zn26i0N}QPo}WV>oq{t1mj4~#!Yc@1nE`;(BUxJ`8R@&XM8k_w{2Gf1*akBSNhV2hJbRVnWs&g#I2PbgaPh1Qr1Ay z^%r$XyVc-EpOaHt^!L|qXi5(+>bkg~Fl)mV@*#;`&vGBjZ*TZU<7ZvJ8yH9^?jV{> z+cq}uEiBx7vXDXyua%Iuz+Lqf&B$(ED$QbSrZKp6)Z54A>H;7NLq7!(BNm>v+S<&` zr(E`1&MUywuS`TQ?OFv~pE=qzaY~{$ot#VGp5Sa~h*yi83@6XFrl|_mjcjapbQUN_ zqm(tH)?W89wu-%QH@@Y)X?3#Kp8oP;v&JD)btdU{vQOx(oq@8J7M|B`^5Oq-)K;4} z_TrITj(ekcpKxl?LPy7}JaOpz90+t{X;R9nLub%-4@JiIE|?jw{oD#_Yvtwfta|bD z?LvqxW{)zJf~w!~(yH8Fz1x|}ao>VlsyCh6-NWP4lct)zKg3nMZ;w>S*#6Ie7f%)z zamI6v7KVLJ+>?Rz$|^#PwI`kV!;JCPa;5ge-#^ZQgzDolQTz)!5t|jL_BYb&uTM`@ zIfeND;F^hYSY6pI{o6G~DSH2Fzd?V`b~)N2_srt&UeWX4{^r=a-J(@V%F6m1y?QUi zuXgiWj(+^rzKQLwJ1-gj{i3o@78kc_c5>JH%+-*9AnYFjzSW$EC+@D4 zn2j*jcgzb8PpXgTBLg*kqxP2rK41U-r)DcS^3_@%w)|M|b^=B|PhO&A&QTN-JetjX z=>5!$m0Y_dTA_9@ge_3Bd_36j_FUb#j~KxEf)FmHEbZ3y&@)J2miFUi7_E1t>=d#3 zg}by_-lgp^7amU^@5uG|?vw)a65^spjl5Iix`n*iHeFFbP*CaA@}rN3oM`V-RvCNr zjMwvzZ@7L;6AgK3XPKNI`DweMv~>1+shYaJUiqlMeT^y-lK-~BU%y&@-#v1Sm(M^_ z@??vIeA8e#XF@_=6hl6kzLVA#wO=LQc-$=S;n|=o@1}fH4E;AFAE~YnL6R)dzo591u@U6>gp46 za_auu-)yyP-)>=4N~Kgje$1uM@N6-xa|(z%pN}8L(C|)ep2ljq2%zy9y^rEj51BxH zEC5WMg|u1#0RE%patKbK@V{1~Bhi_FT6*z(^HaBZDr?EDcZ!yzQSouDh>i9yHwWO5 zG3XANk9z&|26HVtE2X05H17&6PoONF7cvRMiql)0>kp8S#o;L!LZhI`V|6$U6ahAX zDM~o~2H4N!a(+t^CG6%Q>ps8MavkzFHm2W$3tv)dY zq+^1bvdA?d(%I*QA=-*inyW0&Kwf}#AI%BPu7@##I}6Ep3+(Q++ViBNADWJB?0bTU15ljxaf2i=O|Da>_<7HN?<^|6!rwE z6it#qVOyM}al(^lfo-+jtp2t50vgJyqzmK#W5~#q5QBmL4n|kOVEw420%u;q{MvZ^ zLqngzK^bO<5mZFPscbla;0+uNI>Btmk`yhX7pYrI)w$|uz{P!!fSo`seTGJ9YdUY= zM1SSD!%kS?CAD+%0*0(4F)kMYZUhTy0c{C#?}M9R)NtPHigWmpo+}oV7QH~tAV42` zB|aKU(c_mV5>~xVigLlg~jm!oJ|-Fw|%ZPO`=-uzAl#Wz8|~dRm-V2QLBCt3awW zG@}!kdV;kZYt~XePziYHR{)f9U~*e=SyJJtb=pp4eUeoaC_hhFNPZ59mZ{J_=vS2i z3?BpiK0wV8&~FrK4uO7DHh0*_4MqtFt}V8p=i)SwB#SoW735!9hTXXX<~-L1Qw^c@ zMu61wP}2vbnz3e*zV5<0@)Vu}6emEWToM2Xkdt!=kv&hb0Un=#$tAg<6WAeN>-RO5 ziImUe_gl}7`CEX~DPfo)Q4NYZAky8TXvvowJb22Y?OVQzeMDWS zf;Mbe8u3t3h|IRp>G}Q^YAYg_jUo>;;+)W<9eYdJaJwxV!aLpgWkie;K6cl<+_=!%QJWSYls1qN5I1b zV)WH9JjUn!=90vZReq3g&w%jHi}l}X8bg-AM_;73BIyZ6;s!urfrBuT>9&d`Kvo5} zD>k#k2T$>6tj)UMZ;=}Fic^t?-R2b8nJ9Kl9vUpqJekQpe-VTgjl$;heM&KpD52-_ zTgU|(*4*4Z=17~bAbB5w<$;tK3Roy~_yKDm>eJyykg=*;IfoOHkNFVzb%)&w#12zt=t;()BK-Aq#5^NTrSWoMq6=4zd8oDW#`- zwSD7K#z(n}2$E^=9~vbsz(@ zWI70?B!LzNAOnkYExh))NafyNklRi$E{UA$fo_Kdu z@A`D>v~3n2#h*7LWu74BI_ySD30NecCwjHQbd8}Q#iH*O6D1iOE{fQ}caVJ>ezEJ8Bi z>DdwzIB<5|y2*Dt@iHKm4ab>EPj~7+(t|z1RW%|fabmDDyHev?{P85C&6Q(I zLQpKwv5Nw@wO%s+n>U+EX%f?nGVO~P{X!eV1LPa9{sIBQ8TK2h<$??l{iF(}81pb+ z0CW^(6w!QKsoRm_;6_pb$4D`xYQ(_lW zheCSTNdkPa(ZWtqsZ>n~%wMIKOY!b6^Xzu}vwLQm8%QDjea{R4duQ2}{Qbr@*G$~X zOi`ILY24m8#?qLf`c}99y*CDoe|dW5lZ*HIbuxTBCvi<0*!aiP@i#t6#*^c70FUv? zZav}jcn;uafQQ8emEgn7T{J`)V&$N%J?Hh&uUZgs zzFguv9mO#VdLu_(^9@Jlpvwy%)UZ#h-URzpE4;#F;xqul3MPev7ZRm{ zdv_u0i1&8iLvYJ^9Rt9`KBfXdpQ3{%dT&mgeaa2oN>ew;xjz5b07_gsBozQ` z{Szh|=>pK^KFD^R_wVd%#-ovA=kHilztuoqb)5@yhBOdzD+j6$K#m(g=OF;r*Q9!- zrGd(_cK#2WN{^P((VG3ekLhm<3M%r1P`%vT0D%Tl#CMA8%6{eN)wIu=fLIt=W+h}) zK@o)b6rzv+^EuM$mSf<}SX%G{7C?>}>@#G%%G|lu{#p6@i{jA%Km*`Tng8C?#&PMg z{O(cRp|AFi!3&YXM(x8FH3;MES$7&K(WUA)4gZm-T1th@lPdzoon z2Z!%@mI!rYwGZ!CH?RE$u+98T%J=C|C!6W;rwH1S{c0W4E76LWlPcnOHC*c7$P)I; zfykW$^+?{|v4jL()#cASlzlWea4p`oP+CqWvplWgwrl3=+!(YAhfxW0p>P^_0VI2s zu>#plWdPxgeZE6%aO29nEAO!MJyVp|5BjUMqI;(9Ck45sWA5_J1EiN{QlLB(vQz-% zKUtMr0dFzoW)xueGDWTiUd`IoxT35GWuzSc+mY);{EuU^>d5&+&hARqU`E{RH z$Gy@6U;(q(y>2 zhKsHF+Pes|oQ#$3v_{_znzlVN6QG$Wnt7AfrylmPIR6HWPaa-MG@N;@w7qt-FxY#2 zn5tSttb6H*t2}f*^6yY-rv>d_=D9RMe^tJ!tKZCoq419&CW>#OI5{~wI62;$LE1kF zYf)Yg?2LuBgPoxG(#R9m!W3l$lMGLE_(sWRBjDD{b@lX~D>`!8)``jwZPgyydNqf< z86f=@Nd6TS49R)$;jH|dfqN>xh+_!ToAyvR0C4ZC^nefpaCox-LReNzuQ@wIx#pss z#OLK7mpK+XgB!hbJ99ki7#+rCTIk|JGyi+J1sfR0A%bY|TmQ(@c4 z=+|yMf97~&zOCAZ*;LJW_K64f#?P2;aV@Uc{xCc5`rqR`@W<1{2$<}-D3`>eu_uBN z7}LgpS-zw3Xa1w?-09i=;+9NKyg;(zIxl(-@)J&6hd9=9g0NQv`I%{i(wTd`^Mu5N zVn{|(Y{3gatwJJ<#JDyXAGXPyj4U8;M=Qi7i`~k7zjr#+`r?PjqwJfK+*`EGFHyZr zlyZYiD>DoNATbpYUw(MsmIt2US@FdQ6QJ}+!Jb+pTk$uPy1M;pTvi2&Ysia~)zX6# zLL;!hEMT&37vn0Nr+7u$Q|RNVG|@pq0!Kn(f*C4!st)~8&Xn0%IzSyIh{GdG!3DsD zC0iVgeL=C{f-DkQZ8sAR&A9Y>cp}=KdCGJ$bxzvATXpb{vt}Oht4&yNx0lJ+SMVQ? zF2#Mo1%7|lVped{;W)rA02Bro=1LG=#0;=b@)|hW+Fx<0T&_7?&6=3n{I?X(FGYCT zZGB+=q+@z~dbxX92p>PB3I#XPe0z(3j~>=j&IP!=%wPe*sO2gTivsU_pR7AJ!mH0A z><9vuB=5sO0_6-{|KMyDEnUu9XQy&Y+xu;f^o?a?1pa(R-p+m!%WJ>#UUv#^@N6$M z^}l00l_%mQ8K+5!}dxv;J zAvvA?*2r`2#N+S5#4z`*WiGthE&o2xMLyu3%3zc5A1 zv7X9>-`B+As;t?Y-J%ld*K#!oZ$4Ks|BIyhKCkk5Ue!3;S#?^j`m|h-JnQxX%?2S@ zN|36IDf}CwlR&o-VL(9Jtzx{HuBVx9#WJQQ0umbPaYXi6&gS^&UHZUA4(ZDm_md|X zV^1xAO6($)^oV~}TPRc2FHXzxWuy;3hqm1P7IXN>bHee;=LX7get07L z{4P_nQNXP`Yki7(M2C4`ZrAs8dQUMi|7LQx-nEkA$OrJHQ&%Yfw6NGjZR4r0s4J;1vM9<|8{yu+-*Akd@<<@%LfC`h+`tIooOW=En?npMNbmK z^0={E6Z#S-VCa_n49MCBGNw+#JeXMBVOC54VY!uY3FsKGsF#HaGRHy&x^6D$zDxI; z)1^!5mp>Q%?rF2_KdeoXUCL8^%^KN&A zydvbr0Q+D@1+e?k&4ADk8b zzTV|2PYODG3`q)}*nImiA?xe#dqddO(zF8UV)d|M7`fo`cAYjUWOLDDtEHj@cuzV??Y;9@{AW+gu)e zT~>4+%Dezb`=w(*EVWJe`QsUIJ^tL@Y6W|LfVlQCEHrJ8EMZ4qh!s}oOLd3b1e9tm zdc@?zbqT{^qNcuvnhneJ0IN`zSit44p*kM~XTu7i*kH@aj-}F&%jo-cMbbhy_7O95 zazF0aXy$W7yuUP7LR`rt4Fv0jW{w2_6Dpjg@8_34cV0H7x*yP>0SFoZDZsGU0UI;` z^ll~q(UL%iVoltKl>ox%m9edq5)b!Uj&F(v*vfrdrWjYOL$qVAZ0z5%3#gahfWW%3zM{ zT~0rLu-qj;xIhGZ40v2iO{!CdL4hkd2P#0{O7@k z%^A#NZGmPp>U`yp_V~yfe%ypZ?#((8=Z&tKQ}lDX7kD^b%Z6Pb0KqkR|Cz%?!FHe7 zRE4IC#G|r2kLvo5RQb+A1q6wXF!>EA-G6&@1VUNZ2yNWqvrxgq2v|xcpMkVMTyzCN zx{(;_VXp2!^V#3!q`;9cAkQ(YJ0%jhXGK*5wndn$nhu+TKXr;vaqk~{QvnwsNNt&r z1n%A`?r@O&}zYdTm*5O|r5qAHM+6G6oCVkE1Id*ZZb)Iup>?KRA#QTx9 z>lTw9M?r*o=7TK{ZEJi)KSXdBytyowFcJ?MAr-iY`W_&RQmPqsCzXQ=F)ZIxQRvg&gJDdxJo zVr+zMA1Yl@b7TB?+TOo&Jc)Ultsa9;$E15s{@e}OnrC8)f40B0b$X-0wN+J!p&dDW zKl`VNQi0vit5!mS!ui1rl(5O6lgO3HQy~5{2WQgedEwgq*n*4r0;)-;Q{Yk9dB+`( z&qF~for*1Cy@iW5zuVR$P4u58Typb%fyu0fMLd1}??-!fEwytDQ!Y3ETuIXr2a9Ea z7Usvds9yWB6x`=oQPUH(nA6OR3Dj2HZg~{ut37tr>V!m(%0XA#qu)Vq9@$~63zzc;kemzRZFeji~i`b%np7&6(M{DLd$ z=l6h)<5wMnJhBqvEb=bb;Ul6PZODa^vwn@LDvh7{%2Sy-kwN=P>YkxJiR-zx0n;O( z{yD$L1bhJD0(*01Ogjo$B<^N^Wc7Aj^tL!C6R)(gV839FBK}o^!&<}d@*wv=?iYxn z`tb8R3njGPBVhN6Rxl;i+{sWQ56(Nt#n@xvNz7I~E~%ouFul`Dc?TbquRObLkUNji zypo&SGJ2&Hcjb*oobh?kR+{j0TBqM>)kL?xUL@l8+&6rbj?YTZ$AZ1;!Jn!#1sh?< z_2SEZ+*nbY_WGio=1!y*Ce*8{h=m+oWQ-RCR|Z_)T}f+oBUBRiD;r9sB1ff`$GbN6 zbdy(-o;E!Dd*?fx_hOpPcGUGq%(X9%tPw;%hJ4X6>2I$1`S72+gE>X-KMm!XC>=Dj zH%_KGDo^YC&H)j9Db^dSH|oH|I|k3Z8BK94YrV`DA{&QdG$!+vA3~wP75gW-UN2rT zsN_j047<7Tc#l#tCR%H%41b{)&6C(dy)w(Xu%4TLnRYGmhY|=a%qnz7JdpbEmzCjt z!%E~TTY-qA`{7&t$gqFW>?@5#IR4&9C(i)uj_aG)syL4DV6$KZT%RpmOw9$33p8 zn}tK_(K0(H8)?8x7!EaCikK+=+vV4-Cx-ReYclq`D%w zdBzg7WQQKxj>qJz@oJfK876iiZVeK69~&C=*`$(j%Rhwe{G2n4K9joA64I z!n6A>450XNAdhp_#^sTba{yH*pHB|eu$f$<%nX&IK#Iqo*^-lKnnS3sB`2^H1RK^c zx$}4ut0%OU4X8^MnQ+t-c80cY!3ZZ&|ATWoezL)7k6a@Dwf}W(7!=56$iQZp9LM)*fnGRs3(JN3{D;>hx zpmg#EY+5HUpULedPT8J$;i+%rXB@XD)Im2aS@E(8+>Lm;EJ7tj8E%giVQE<>U`c*~{RSmJW{e z8>63nt-@0`F8OcB*XXMMtuFGjUrSf7l*o#oF#ZaXUftDmzx;0GPYgE_itoJ2<9*^% zO!+c8gojT2e3og#LpIxT!ofTa0jilzKJXNBrp+~hbOmPMN!Lp!R~ZBBk1;%y^%Eqp zBb(wNP#bVRASiU|CZ}!i4Ivrys5!aUMDK{x(FYGM<-W(J$4MR+x;6dleFXpvpG7oZ zL-kL1aIXC*MaNooemqS}WDl%YReO4Q-^j|Co^T|@jlO-6M`$NrJLob3cg4E!b>w2J zKtWAFgeC4-_{8H+#d*)eF#H+m7p%O}Rs=F$_4rDUZnU;k!HXrGv364<7DHk`PfKLH ze$3UfJXM95Q?cbwHu_!VnqfOOdeL_5qOH~6-RovjI}+33UWTT615xt%(R9Pq=kJzP z(`MpffK3)LTEAo3D~0vB%ZbVJ(L0N3GK_o#H*s3+jr{1yrEs^&`WJ}($(XlU(Q~_2 zUIEv8E42(H(dv?G?MLhvyj}$R`fF1zHf-@73vLzjZCgJxvo-nq!WyG~;aiDXlF{(9 zpRNUOc~o!PeB30)r?#HjdvNzBv9+zCNXNTFU`-Z<*{3?prv|D3aMUWOWNCKQH#_*LO3;M3P+s7zvCVoMllDv24LfXC8bD!#zm$o;0^eA1hofscE zQiF;5%A;+EyF>;AL@w5CjMW<3CK*vxWRr&4$-gY+CB7pXRSvtoAg$;4{uL!w?nOF` zt=$)cZUZFG0b5-jtGY$OsMq>2E<*uUh3Hq?-d!4% zxCT|Ve8+~4Tfyc*dt)1U6ffIzy>=H{ZU7^JS3Nlscq)eAEN{N#weq&aI~4#-X$af7!{OKJ9LU8Sufnb-`a4Kw4H3I3wF&*zSLzWhm zhV9?mt15ih?_xFgO>8`ODo&g5NQ%BQU88u}CN*`Cx z^5M5C^E!Q(-PQq;&)tsx)=z`%PZDyqOmLNHuKoN2%d>^aIzP`_VG((396*QJ)H~0Y zg^35(oDR_F5BXb6WupQt>R7%Qb^s`}usyzxy3A6UF|6?#_e?tTQQ=co(F|#ckGYrO z>=1MY8Pvw5g;-UP4G?yTyt4@!0fC`^{tgIpKZa1ZCqe#A-YR9WI}4@DEq&8H_MUu{ zh8Yy&xDEJ6=H4v!Rx3k_gN_x1#fsez?Ia`mQIMI2LzlqVfGcyr*Z^Vi7*uG1L1k2I zf$}8%Od_3}_vIR|phW+tmcoFW%jy~;1FCJ0c%g5)T5_2P0iKhUmo+254xEmQ#^NF) ztU~Ri6n`l~?gP3Hk?;t>9bEtNVMqS|J0QWcb_JIkLvQeNf`FzCu$cC!pBpHAw9)yc z*Br(mUjGJF=q^xgR|0^8o-s8mHIwukN;M^SLte2%Fb|-KWAtXlJb(kF&3-4G$kTD* r$o;y1QYG1ZEI_VZNPX4ydIrO~;8Ebf_Xcl>U4XHHIqs#NbL{^BlnRR8 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_late_night_drive.png b/app/src/main/res/drawable-nodpi/cover_late_night_drive.png new file mode 100644 index 0000000000000000000000000000000000000000..87bb83b14b97d8aed4c1e5babe9fa892775ce918 GIT binary patch literal 21430 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_long_way_home.png b/app/src/main/res/drawable-nodpi/cover_long_way_home.png new file mode 100644 index 0000000000000000000000000000000000000000..25e050748b12114e08038424fc95092b8079365e GIT binary patch literal 17157 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_lumen_park.png b/app/src/main/res/drawable-nodpi/cover_lumen_park.png new file mode 100644 index 0000000000000000000000000000000000000000..77e0296389e65baa28cca91101234b8e42bd0228 GIT binary patch literal 9983 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 nlS}+R=eyi<3&j6`SN literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_mira_sol.png b/app/src/main/res/drawable-nodpi/cover_mira_sol.png new file mode 100644 index 0000000000000000000000000000000000000000..dbbfb1beedc83720883976ae776e79c6583473a7 GIT binary patch literal 12819 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_soft_focus.png b/app/src/main/res/drawable-nodpi/cover_soft_focus.png new file mode 100644 index 0000000000000000000000000000000000000000..d773446e81dfeb92284fb9477046ec2b78da36bf GIT binary patch literal 19114 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-nodpi/cover_sunday_morning.png b/app/src/main/res/drawable-nodpi/cover_sunday_morning.png new file mode 100644 index 0000000000000000000000000000000000000000..4ede9ae534d98df1d9dd21bb8b6c4e6b7da74ca5 GIT binary patch literal 14411 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 literal 0 HcmV?d00001 diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt new file mode 100644 index 00000000..48ed6fa9 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -0,0 +1,170 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.compose.runtime.Composable +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onRoot +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.takahirom.roborazzi.captureRoboImage +import hrln.interactive.sidekit.ui.SidekitPreviewFrame +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.GraphicsMode + +@RunWith(AndroidJUnit4::class) +@GraphicsMode(GraphicsMode.Mode.NATIVE) +class SidetracksScreenshotTest { + @get:Rule val compose = createComposeRule() + + private fun shoot(name: String, content: @Composable () -> Unit) { + compose.setContent { SidekitPreviewFrame(content) } + compose.onRoot().captureRoboImage("screenshots/$name.png") + } + + private val recent = MockSpotifyDataSource.recentlyPlayed + private val current = MockSpotifyDataSource.recentlyPlayed.first() + private val state = SpotifyUiState( + current = current, + upcoming = recent.drop(1), + history = recent.drop(3), + isPlaying = true, + positionMs = 68_000L, + savedTrackIds = setOf(current.id), + filter = LibraryFilter.Albums, + ) + + @Test fun home() = shoot("sidetracks_home") { + SidetracksHomeScreen( + state, + onNowPlaying = {}, + onLibrary = {}, + onSearch = {}, + onSettings = {}, + onPlay = {}, + onDismissNotice = {}, + ) + } + + @Test fun library() = shoot("sidetracks_library_albums") { + LibraryScreen( + state, + onBack = {}, + onSearch = {}, + onFilter = {}, + onTrack = {}, + onPlaylist = {}, + onAlbum = {}, + onArtist = {}, + onShow = {}, + onLoadMore = {}, + onDismissNotice = {}, + ) + } + + @Test fun album() = shoot("sidetracks_album") { + CollectionScreen( + MockSpotifyDataSource.collection(SpotifyCollectionType.Album, "night_signals")!!, + state, + onBack = {}, + onPlay = {}, + onItem = {}, + onSave = {}, + onArtist = {}, + onQueue = {}, + onNowPlaying = {}, + onLoadMore = {}, + onDismissNotice = {}, + ) + } + + @Test fun artist() = shoot("sidetracks_artist") { + val artist = MockSpotifyDataSource.artists.first() + ArtistScreen( + artist, + MockSpotifyDataSource.albumsBy(artist), + MockSpotifyDataSource.topTracks(artist), + state, + onBack = {}, + onPlay = {}, + onTrack = {}, + onAlbum = {}, + onFollow = {}, + onNowPlaying = {}, + onDismissNotice = {}, + ) + } + + @Test fun search() = shoot("sidetracks_search") { + SpotifySearchScreen( + state.copy(query = "night"), + onBack = {}, + onQuery = {}, + onClear = {}, + onToggleScope = {}, + onTrack = {}, + onArtist = {}, + onAlbum = {}, + onPlaylist = {}, + onShow = {}, + onDismissNotice = {}, + ) + } + + @Test fun nowPlaying() = shoot("sidetracks_now_playing") { + SpotifyNowPlayingScreen( + state, + onBack = {}, + onPlayPause = {}, + onPrevious = {}, + onNext = {}, + onSeek = {}, + onShuffle = {}, + onRepeat = {}, + onSave = {}, + onQueue = {}, + onAlbum = {}, + onArtist = {}, + onAddToPlaylist = {}, + onDismissNotice = {}, + ) + } + + @Test fun queue() = shoot("sidetracks_queue") { + QueueScreen( + state, + onBack = {}, + onNowPlaying = {}, + onJump = {}, + onPlayNext = {}, + onRemove = {}, + onClearUpcoming = { emptyList() }, + onRestoreUpcoming = {}, + onShuffle = {}, + onDismissNotice = {}, + ) + } + + @Test fun settings() = shoot("sidetracks_settings") { + SpotifySettingsScreen( + state, + onBack = {}, + onAudioQuality = {}, + onVolume = {}, + onClearCache = {}, + onClearHistory = {}, + onSignOut = {}, + onNotice = {}, + onDismissNotice = {}, + ) + } + + @Test fun signIn() = shoot("sidetracks_sign_in") { + SpotifySignInScreen( + SpotifyUiState(signedIn = false), + onBack = {}, + onLogIn = {}, + onCopyLink = {}, + onDismissNotice = {}, + ) + } +} From 572756c75999c68d85e82d2e6b573449ed86ae67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 20:32:08 +0100 Subject: [PATCH 07/46] Document the Sidetracks mockup --- app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt index 5b2638f0..91253b94 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt @@ -21,7 +21,7 @@ enum class SubApp(val label: String, val icon: SidekitIcon, val designed: Boolea Files("Files", SidekitIcon.FolderCopy), Maps("Maps", SidekitIcon.Map), Music("Music", SidekitIcon.MusicNote2), - Sidetracks("Sidetracks", SidekitIcon.MusicNote2), + Sidetracks("Sidetracks", SidekitIcon.QueueMusic), Clock("Clock", SidekitIcon.Alarm), Video("Video", SidekitIcon.SmartDisplay), Vault("Vault", SidekitIcon.Encrypted), From 64af6f35571e933fbdf0444c27bb6904a79af3bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 20:55:40 +0100 Subject: [PATCH 08/46] Move the Sidetracks plan into the docs and record the reference policy --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 628376a0..c5b37965 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ build/ # Local /Video + +# Local clean-room reference (never committed) +/.sidespot/ From 091a49f4529e986e702ba9ea6ca17c765c592727 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 20:55:40 +0100 Subject: [PATCH 09/46] Give Sidetracks a domain queue, history and catalog seam --- .../apps/spotify/MockSpotifyDataSource.kt | 19 +- .../sidekit/apps/spotify/SpotifyGraph.kt | 8 +- .../apps/spotify/SpotifyLibraryScreens.kt | 4 +- .../apps/spotify/SpotifySearchScreen.kt | 6 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 267 +++++++++++------- .../apps/spotify/data/SpotifyCatalog.kt | 25 ++ .../spotify/data/SpotifyHistoryRepository.kt | 51 ++++ .../spotify/data/SpotifyQueueRepository.kt | 199 +++++++++++++ .../apps/spotify/SidetracksScreenshotTest.kt | 23 +- .../spotify/SpotifyHistoryRepositoryTest.kt | 60 ++++ .../spotify/SpotifyQueueRepositoryTest.kt | 157 ++++++++++ 11 files changed, 698 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt index cdb5dcbc..f81bb6dd 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt @@ -1,12 +1,13 @@ package hrln.interactive.sidekit.apps.spotify import hrln.interactive.sidekit.R +import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog /** * The mockup's account. Everything here is local, fixed data shaped like the gateway response: * the UI can be reviewed on a device without a Spotify login, a native library, or a network. */ -object MockSpotifyDataSource { +object MockSpotifyDataSource : SpotifyCatalog { private fun t( id: String, title: String, @@ -178,13 +179,15 @@ object MockSpotifyDataSource { nightSignals[5], glassHours[1], coastline[6], roomsWeLeft[5] ) + override fun recentlyPlayed(): List = recentlyPlayed + fun album(id: String): SpotifyAlbum? = albums.firstOrNull { it.id == id } fun playlist(id: String): SpotifyPlaylist? = playlists.firstOrNull { it.id == id } fun show(id: String): SpotifyShow? = shows.firstOrNull { it.id == id } - fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } + override fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } fun track(id: String): SpotifyTrack? = albums.flatMap { it.tracks }.firstOrNull { it.id == id } - fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { + override fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { SpotifyCollectionType.Album -> album(id)?.let { SpotifyCollection(type, id, it.title, "${it.artist} · ${it.year} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.artistId) } @@ -198,11 +201,13 @@ object MockSpotifyDataSource { } } - fun topTracks(artist: SpotifyArtist, limit: Int = 10): List = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks }.take(limit) + override fun topTracks(artist: SpotifyArtist): List = topTracks(artist, 10) + + fun topTracks(artist: SpotifyArtist, limit: Int): List = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks }.take(limit) - fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) + override fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) - fun library(filter: LibraryFilter): List = when (filter) { + override fun library(filter: LibraryFilter): List = when (filter) { LibraryFilter.LikedSongs -> liked LibraryFilter.Playlists -> playlists LibraryFilter.Albums -> albums @@ -210,7 +215,7 @@ object MockSpotifyDataSource { LibraryFilter.Shows -> shows } - fun search(query: String, scopes: Set): SpotifySearchResults { + override fun search(query: String, scopes: Set): SpotifySearchResults { val q = query.trim() if (q.length < 2) return SpotifySearchResults() fun match(vararg text: String) = text.any { it.contains(q, ignoreCase = true) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index e20b0027..4578071f 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -81,7 +81,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { val state by vm.state.collectAsStateWithLifecycle() val type = SpotifyCollectionType.entries.firstOrNull { it.route == entry.arguments?.getString("type") } val id = entry.arguments?.getString("id").orEmpty() - val collection = type?.let { MockSpotifyDataSource.collection(it, id) } + val collection = type?.let { vm.collection(it, id) } if (collection == null) { MissingCollectionScreen(onBack = { nav.popBackStack() }) } else { @@ -105,12 +105,12 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(ARTIST, arguments = artistArgs) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() - val artist = MockSpotifyDataSource.artist(entry.arguments?.getString("id").orEmpty()) + val artist = vm.artist(entry.arguments?.getString("id").orEmpty()) if (artist == null) { MissingCollectionScreen(onBack = { nav.popBackStack() }) } else { - val albums = MockSpotifyDataSource.albumsBy(artist) - val tracks = MockSpotifyDataSource.topTracks(artist) + val albums = vm.albumsBy(artist) + val tracks = vm.topTracks(artist) ArtistScreen( artist, albums, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 9370af00..d4864884 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -39,7 +39,7 @@ fun SidetracksHomeScreen( onPlay: (SpotifyTrack) -> Unit, onDismissNotice: () -> Unit ) { - val recent = MockSpotifyDataSource.recentlyPlayed.take(10) + val recent = state.recent.take(10) val bar = withNotice( state.notice, onDismissNotice, @@ -138,7 +138,7 @@ fun LibraryScreen( val first = remember { FocusRequester() } LaunchedEffect(state.filter) { runCatching { first.requestFocus() } } SidekitList(padding, state = listState) { - val items = MockSpotifyDataSource.library(state.filter) + val items = state.libraryItems if (items.isEmpty()) { item(key = "empty") { EmptyMessage(state.filter.empty, "Search to find something to save") } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 5abc0231..70cb5894 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -41,7 +41,7 @@ fun SpotifySearchScreen( onDismissNotice: () -> Unit ) { var expandedTracks by remember(state.query) { mutableStateOf(false) } - val results = MockSpotifyDataSource.search(state.query, state.searchScopes) + val results = state.searchResults val actions = withNotice( state.notice, onDismissNotice, @@ -93,6 +93,10 @@ fun SpotifySearchScreen( EmptyMessage("Keep typing", "Type at least two letters to search") } + state.searchPending && results.isEmpty -> item(key = "loading") { + EmptyMessage("Searching…", "Spotify is looking for matches") + } + results.isEmpty -> item(key = "noResults") { EmptyMessage("No results for \"${state.query.trim()}\"", "Try another spelling or turn on more scopes in Options") } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index fb911bbe..135a0289 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -2,6 +2,10 @@ package hrln.interactive.sidekit.apps.spotify import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog +import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository +import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository +import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,6 +18,10 @@ data class SpotifyUiState( val current: SpotifyMediaItem? = null, val upcoming: List = emptyList(), val history: List = emptyList(), + val recent: List = emptyList(), + val libraryItems: List = emptyList(), + val searchResults: SpotifySearchResults = SpotifySearchResults(), + val searchPending: Boolean = false, val isPlaying: Boolean = false, val positionMs: Long = 0L, val shuffle: Boolean = false, @@ -32,17 +40,26 @@ data class SpotifyUiState( } /** - * One state holder for the mockup. It owns the queue, a reasonable simulation of playback and - * every optimistic save/follow, so navigation never resets the visible account. + * State holder for the Sidetracks UI. Catalog reads, queue ordering and local history live in + * their own domain objects; this class only maps them to the single immutable screen state and + * implements the optimistic save/follow behaviour the action bars expect. */ -class SpotifyViewModel : ViewModel() { +class SpotifyViewModel( + private val catalog: SpotifyCatalog = MockSpotifyDataSource, + private val history: SpotifyHistoryRepository = SpotifyHistoryRepository() +) : ViewModel() { private val _state = MutableStateFlow(SpotifyUiState()) val state: StateFlow = _state.asStateFlow() - private var context: List = emptyList() - private var contextIndex = 0 + private val queue = SpotifyQueueRepository() + private var searchJob: Job? = null init { + if (history.items().isEmpty()) { + // Seed the reference account in the same order the fixture lists it: most recent first. + catalog.recentlyPlayed().asReversed().forEach { history.record(it, 0L) } + } + loadAccount() viewModelScope.launch { while (true) { delay(1_000L) @@ -55,6 +72,14 @@ class SpotifyViewModel : ViewModel() { fun notice(text: String) = _state.update { it.copy(notice = text) } + fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = catalog.collection(type, id) + + fun artist(id: String): SpotifyArtist? = catalog.artist(id) + + fun albumsBy(artist: SpotifyArtist): List = catalog.albumsBy(artist) + + fun topTracks(artist: SpotifyArtist): List = catalog.topTracks(artist) + fun play(items: List, index: Int) { if (index !in items.indices) return val item = items[index] @@ -62,38 +87,36 @@ class SpotifyViewModel : ViewModel() { notice("Not available on this account") return } - val previous = _state.value.current - context = items - contextIndex = index - _state.update { - it.copy( - current = item, - upcoming = items.drop(index + 1), - history = if (previous == null || previous.id == item.id) it.history else (listOf(previous) + it.history).take(HISTORY_LIMIT), - isPlaying = true, - positionMs = 0L, - notice = null - ) - } + val snapshot = queue.play(items, index) + recorded(item) + applyQueue(snapshot, positionMs = 0L, playing = true) + _state.update { it.copy(notice = null) } } fun playItem(item: SpotifyMediaItem) = play(listOf(item), 0) fun playCollection(collection: SpotifyCollection, index: Int) = play(collection.items, index) - fun playArtist(artist: SpotifyArtist) = play(MockSpotifyDataSource.topTracks(artist), 0) + fun playArtist(artist: SpotifyArtist) = play(catalog.topTracks(artist), 0) fun playLibraryTrack(track: SpotifyTrack) { - val recent = MockSpotifyDataSource.recentlyPlayed - val at = recent.indexOfFirst { it.id == track.id }.takeIf { it >= 0 } ?: 0 - play(recent, at) + val recent = _state.value.recent + val at = recent.indexOfFirst { it.id == track.id } + if (at >= 0) play(recent, at) else playItem(track) } fun togglePlayPause() = _state.update { if (it.current == null) it else it.copy(isPlaying = !it.isPlaying) } - fun next() = advance(auto = false) + fun next() { + val before = _state.value.current + val snapshot = queue.next(auto = false) + if (snapshot.current !== before) { + snapshot.current?.let(::recorded) + applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + } + } fun previous() { val s = _state.value @@ -102,19 +125,12 @@ class SpotifyViewModel : ViewModel() { _state.update { it.copy(positionMs = 0L) } return } - val previous = s.history.firstOrNull() - if (previous == null) { + val snapshot = queue.previous(s.positionMs) + if (snapshot.current !== s.current) { + snapshot.current?.let(::recorded) + applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + } else { _state.update { it.copy(positionMs = 0L) } - return - } - _state.update { - it.copy( - current = previous, - upcoming = listOfNotNull(s.current) + it.upcoming, - history = it.history.drop(1), - positionMs = 0L, - isPlaying = previous.playable - ) } } @@ -123,12 +139,9 @@ class SpotifyViewModel : ViewModel() { it.copy(positionMs = positionMs.coerceIn(0L, duration)) } - fun toggleShuffle() = _state.update { - val on = !it.shuffle - it.copy(shuffle = on, upcoming = if (on) it.upcoming.shuffled() else it.upcoming) - } + fun toggleShuffle() = applyQueue(queue.toggleShuffle()) - fun cycleRepeat() = _state.update { it.copy(repeat = it.repeat.next()) } + fun cycleRepeat() = applyQueue(queue.cycleRepeat()) fun toggleSaveCurrent() { val item = _state.value.current ?: return @@ -171,23 +184,37 @@ class SpotifyViewModel : ViewModel() { it.copy(followedArtistIds = ids, notice = if (wasFollowing) "Unfollowed ${artist.name}" else "Following ${artist.name}") } - fun setFilter(filter: LibraryFilter) = _state.update { it.copy(filter = filter) } + fun setFilter(filter: LibraryFilter) = _state.update { + it.copy(filter = filter, libraryItems = catalog.library(filter)) + } - fun setQuery(query: String) = _state.update { it.copy(query = query) } + fun setQuery(query: String) { + _state.update { it.copy(query = query) } + search(debounceMs = 250L) + } - fun clearQuery() = _state.update { it.copy(query = "") } + fun clearQuery() { + searchJob?.cancel() + _state.update { it.copy(query = "", searchResults = SpotifySearchResults(), searchPending = false) } + } - fun toggleScope(scope: SearchScope) = _state.update { - val scopes = if (scope in it.searchScopes) it.searchScopes - scope else it.searchScopes + scope - it.copy(searchScopes = scopes) + fun toggleScope(scope: SearchScope) { + _state.update { + val scopes = if (scope in it.searchScopes) it.searchScopes - scope else it.searchScopes + scope + it.copy(searchScopes = scopes) + } + search(debounceMs = 0L) } fun cycleAudioQuality() = _state.update { it.copy(audioQuality = it.audioQuality.next()) } fun setVolume(value: Float) = _state.update { it.copy(volume = value.coerceIn(0f, 1f)) } - fun clearHistory() = _state.update { - it.copy(history = emptyList(), notice = "Listening history cleared") + fun clearHistory() { + history.clear() + val snapshot = queue.clearHistory() + applyQueue(snapshot) + _state.update { it.copy(recent = emptyList(), notice = "Listening history cleared") } } fun clearCache() = _state.update { it.copy(notice = "Artwork cache cleared") } @@ -196,25 +223,28 @@ class SpotifyViewModel : ViewModel() { it.copy(notice = "Added to Late Night Drive") } - fun moveToNext(item: SpotifyMediaItem) = _state.update { - if (item.id == it.current?.id) it else it.copy(upcoming = listOf(item) + it.upcoming.filterNot { queued -> queued.id == item.id }) + fun moveToNext(item: SpotifyMediaItem) { + applyQueue(queue.playNext(item)) + _state.update { it.copy(notice = "Playing next") } } - fun removeUpcoming(item: SpotifyMediaItem) = _state.update { - it.copy(upcoming = it.upcoming.filterNot { queued -> queued.id == item.id }, notice = "Removed from queue") + fun removeUpcoming(item: SpotifyMediaItem) { + applyQueue(queue.removeUpcoming(item)) + _state.update { it.copy(notice = "Removed from queue") } } - fun addToQueue(item: SpotifyMediaItem) = _state.update { - it.copy(upcoming = it.upcoming + item, notice = "Added to queue") + fun addToQueue(item: SpotifyMediaItem) { + applyQueue(queue.addToQueue(item)) + _state.update { it.copy(notice = "Added to queue") } } fun clearUpcoming(): List { - val removed = _state.value.upcoming - _state.update { it.copy(upcoming = emptyList()) } + val removed = queue.clearUpcoming() + applyQueue(queue.snapshot) return removed } - fun restoreUpcoming(items: List) = _state.update { it.copy(upcoming = items + it.upcoming) } + fun restoreUpcoming(items: List) = applyQueue(queue.restoreUpcoming(items)) fun jumpTo(id: String) { val s = _state.value @@ -222,36 +252,83 @@ class SpotifyViewModel : ViewModel() { _state.update { it.copy(positionMs = 0L, isPlaying = true) } return } - val at = s.upcoming.indexOfFirst { it.id == id } - if (at >= 0) { - val skipped = s.upcoming.take(at) - val target = s.upcoming[at] - _state.update { - it.copy( - current = target, - upcoming = it.upcoming.drop(at + 1), - history = (listOfNotNull(it.current) + skipped).take(HISTORY_LIMIT), - positionMs = 0L, - isPlaying = target.playable - ) - } - return + val snapshot = queue.jumpTo(id) + if (snapshot.current !== s.current) { + snapshot.current?.let(::recorded) + applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) } - s.history.firstOrNull { it.id == id }?.let { playItem(it) } } fun signOut() { + queue.stop() _state.update { SpotifyUiState( signedIn = false, savedTrackIds = emptySet(), savedCollectionKeys = emptySet(), - followedArtistIds = emptySet() + followedArtistIds = emptySet(), + recent = history.items().filterIsInstance() ) } } - fun signIn() = _state.update { SpotifyUiState(signedIn = true) } + fun signIn() { + _state.update { SpotifyUiState(signedIn = true) } + loadAccount() + } + + private fun loadAccount() { + _state.update { + it.copy( + recent = history.items().filterIsInstance().ifEmpty { catalog.recentlyPlayed() }, + libraryItems = catalog.library(it.filter) + ) + } + } + + private fun applyQueue( + snapshot: SpotifyQueueRepository.Snapshot, + positionMs: Long? = null, + playing: Boolean? = null + ) = _state.update { + it.copy( + current = snapshot.current, + upcoming = snapshot.upcoming, + history = snapshot.history, + shuffle = snapshot.shuffle, + repeat = snapshot.repeat, + positionMs = positionMs ?: it.positionMs, + isPlaying = playing ?: it.isPlaying + ) + } + + private fun recorded(item: SpotifyMediaItem) { + if (item !is SpotifyTrack) return + history.record(item, System.currentTimeMillis()) + _state.update { it.copy(recent = history.items().filterIsInstance()) } + } + + private fun search(debounceMs: Long) { + searchJob?.cancel() + val query = _state.value.query + if (query.trim().length < 2) { + _state.update { it.copy(searchResults = SpotifySearchResults(), searchPending = false) } + return + } + _state.update { it.copy(searchPending = true) } + searchJob = viewModelScope.launch { + delay(debounceMs) + val scopes = _state.value.searchScopes + val results = catalog.search(query, scopes) + _state.update { + if (it.query == query && it.searchScopes == scopes) { + it.copy(searchResults = results, searchPending = false) + } else { + it + } + } + } + } private fun tick() { val s = _state.value @@ -263,39 +340,35 @@ class SpotifyViewModel : ViewModel() { } else { when (s.repeat) { RepeatMode.Track -> _state.update { it.copy(positionMs = 0L) } - RepeatMode.Context -> if (context.isNotEmpty()) play(context, 0) else advance(auto = true) - RepeatMode.Off -> advance(auto = true) + RepeatMode.Context -> advance(auto = true, forceContextLoop = true) + RepeatMode.Off -> advance(auto = true, forceContextLoop = false) } } } - private fun advance(auto: Boolean) { + private fun advance(auto: Boolean, forceContextLoop: Boolean) { val s = _state.value val current = s.current ?: return - val next = s.upcoming.firstOrNull() - if (next != null) { - _state.update { - it.copy( - current = next, - upcoming = it.upcoming.drop(1), - history = (listOf(current) + it.history).take(HISTORY_LIMIT), - positionMs = 0L, - isPlaying = next.playable - ) - } + val hasNext = s.upcoming.isNotEmpty() + if (!hasNext && forceContextLoop) { + val snapshot = queue.next(auto = true) + snapshot.current?.let(::recorded) + applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + return + } + if (!hasNext && !auto) return + if (!hasNext) { + _state.update { it.copy(isPlaying = false, positionMs = 0L) } return } - if (!auto) return - when (s.repeat) { - RepeatMode.Context -> if (context.isNotEmpty()) play(context, 0) - RepeatMode.Track -> _state.update { it.copy(positionMs = 0L) } - RepeatMode.Off -> _state.update { it.copy(isPlaying = false, positionMs = 0L) } + val snapshot = queue.next(auto = true) + if (snapshot.current !== current) { + snapshot.current?.let(::recorded) + applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + } else { + _state.update { it.copy(isPlaying = false, positionMs = 0L) } } } private fun collectionKey(collection: SpotifyCollection) = "${collection.type.route}:${collection.id}" - - private companion object { - const val HISTORY_LIMIT = 20 - } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt new file mode 100644 index 00000000..7fe935a5 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt @@ -0,0 +1,25 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SearchScope +import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum +import hrln.interactive.sidekit.apps.spotify.SpotifyArtist +import hrln.interactive.sidekit.apps.spotify.SpotifyCollection +import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack + +/** + * Read side of the Spotify account, as the UI needs it. Native and Web API implementations will + * sit behind this interface; the mockup uses the local catalog. No screen or ViewModel may call + * the mock data source directly. + */ +interface SpotifyCatalog { + fun recentlyPlayed(): List + fun library(filter: LibraryFilter): List + fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? + fun artist(id: String): SpotifyArtist? + fun albumsBy(artist: SpotifyArtist): List + fun topTracks(artist: SpotifyArtist): List + fun search(query: String, scopes: Set): SpotifySearchResults +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt new file mode 100644 index 00000000..4d7602e4 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt @@ -0,0 +1,51 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem + +/** A played item and when it last started. Timestamps are local and never uploaded. */ +data class SpotifyHistoryEntry( + val item: SpotifyMediaItem, + val playedAtMs: Long +) + +/** + * Recently played history for Home. Deduplicated by item id, most-recent-first, and bounded so + * a long-lived account cannot grow the file without limit. + * + * Persistence is injected as a pair of lambdas so JVM tests use memory and the app can use the + * same atomic-file convention as the rest of Sidekit. + */ +class SpotifyHistoryRepository( + private val limit: Int = DEFAULT_LIMIT, + private val load: () -> List = { emptyList() }, + private val save: (List) -> Unit = {} +) { + private val entries = ArrayDeque() + + init { + load().take(limit).forEach(entries::addLast) + } + + fun snapshot(): List = entries.toList() + + fun items(): List = entries.map { it.item } + + /** Marks [item] as most recently played and persists the new bounded list. */ + fun record(item: SpotifyMediaItem, playedAtMs: Long): List { + entries.removeAll { it.item.id == item.id } + entries.addFirst(SpotifyHistoryEntry(item, playedAtMs)) + while (entries.size > limit) entries.removeLast() + save(entries.toList()) + return snapshot() + } + + fun clear(): List { + entries.clear() + save(emptyList()) + return emptyList() + } + + private companion object { + const val DEFAULT_LIMIT = 200 + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt new file mode 100644 index 00000000..3e29a013 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt @@ -0,0 +1,199 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.RepeatMode +import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem +import kotlin.random.Random + +/** + * The queue is owned by Sidekit, not librespot: librespot plays one item at a time and this + * repository decides what comes next. + * + * History and upcoming are plain ordered lists. Shuffle is a view over upcoming only, so the + * current item and history never move. The repository is deliberately free of Android and + * Compose types; playback, persistence and the UI observe it through [snapshot]. + */ +class SpotifyQueueRepository(private val random: Random = Random.Default) { + data class Snapshot( + val current: SpotifyMediaItem? = null, + val upcoming: List = emptyList(), + val history: List = emptyList(), + val shuffle: Boolean = false, + val repeat: RepeatMode = RepeatMode.Off, + val context: List = emptyList() + ) + + var snapshot: Snapshot = Snapshot() + private set + + private var shuffleBackup: List? = null + + /** + * Starts playback from [index] within [items], replacing the queue. The rows after the + * chosen item become upcoming; the item that was playing moves into history. + */ + fun play(items: List, index: Int): Snapshot { + if (index !in items.indices) return snapshot + val item = items[index] + if (!item.playable) return snapshot + + val previous = snapshot.current + val nextUpcoming = items.drop(index + 1).let { if (snapshot.shuffle) it.shuffled(random) else it } + snapshot = snapshot.copy( + current = item, + upcoming = nextUpcoming, + history = if (previous == null || previous.id == item.id) { + snapshot.history + } else { + (listOf(previous) + snapshot.history).take(HISTORY_LIMIT) + }, + context = items + ) + shuffleBackup = if (snapshot.shuffle) items.drop(index + 1) else null + return snapshot + } + + fun playItem(item: SpotifyMediaItem): Snapshot = play(listOf(item), 0) + + /** Stops without clearing the queue; the current item stays so the user can resume. */ + fun stop(): Snapshot { + snapshot = snapshot.copy(current = null, upcoming = emptyList(), history = emptyList(), context = emptyList()) + shuffleBackup = null + return snapshot + } + + fun toggleShuffle(): Snapshot { + if (snapshot.shuffle) { + snapshot = snapshot.copy(shuffle = false, upcoming = shuffleBackup ?: snapshot.upcoming) + shuffleBackup = null + } else { + shuffleBackup = snapshot.upcoming + snapshot = snapshot.copy(shuffle = true, upcoming = snapshot.upcoming.shuffled(random)) + } + return snapshot + } + + fun clearHistory(): Snapshot { + snapshot = snapshot.copy(history = emptyList()) + return snapshot + } + + fun cycleRepeat(): Snapshot { + snapshot = snapshot.copy(repeat = snapshot.repeat.next()) + return snapshot + } + + fun setRepeat(mode: RepeatMode): Snapshot { + snapshot = snapshot.copy(repeat = mode) + return snapshot + } + + /** + * Moves to the next upcoming item. [auto] distinguishes the end of a track from the user + * pressing Next: an empty queue only stops automatically. + */ + fun next(auto: Boolean): Snapshot { + val current = snapshot.current ?: return snapshot + if (auto && snapshot.repeat == RepeatMode.Track) return snapshot + val next = snapshot.upcoming.firstOrNull() + if (next != null) { + snapshot = snapshot.copy( + current = next, + upcoming = snapshot.upcoming.drop(1), + history = (listOf(current) + snapshot.history).take(HISTORY_LIMIT) + ) + return snapshot + } + if (!auto) return snapshot + return when (snapshot.repeat) { + RepeatMode.Context -> replayContext() + + RepeatMode.Track -> snapshot + + RepeatMode.Off -> { + snapshot = snapshot.copy(current = null, history = snapshot.history) + snapshot + } + } + } + + /** User pressed Previous. Restarts when past the 3 s threshold. */ + fun previous(positionMs: Long): Snapshot { + if (snapshot.current == null) return snapshot + if (positionMs > RESTART_THRESHOLD_MS) return snapshot + val previous = snapshot.history.firstOrNull() ?: return snapshot + snapshot = snapshot.copy( + current = previous, + upcoming = listOfNotNull(snapshot.current) + snapshot.upcoming, + history = snapshot.history.drop(1) + ) + return snapshot + } + + fun jumpTo(id: String): Snapshot { + val current = snapshot.current ?: return snapshot + if (current.id == id) return snapshot + val at = snapshot.upcoming.indexOfFirst { it.id == id } + if (at >= 0) { + val skipped = snapshot.upcoming.take(at) + val target = snapshot.upcoming[at] + snapshot = snapshot.copy( + current = target, + upcoming = snapshot.upcoming.drop(at + 1), + history = (listOf(current) + skipped + snapshot.history).take(HISTORY_LIMIT) + ) + return snapshot + } + snapshot.history.firstOrNull { it.id == id }?.let { playItem(it) } + return snapshot + } + + /** Inserts directly after the current item; an id already upcoming is moved, not duplicated. */ + fun playNext(item: SpotifyMediaItem): Snapshot { + if (item.id == snapshot.current?.id) return snapshot + val remaining = snapshot.upcoming.filterNot { it.id == item.id } + snapshot = snapshot.copy(upcoming = listOf(item) + remaining) + return snapshot + } + + fun addToQueue(item: SpotifyMediaItem): Snapshot { + snapshot = snapshot.copy(upcoming = snapshot.upcoming + item) + return snapshot + } + + fun removeUpcoming(item: SpotifyMediaItem): Snapshot { + snapshot = snapshot.copy(upcoming = snapshot.upcoming.filterNot { it.id == item.id }) + return snapshot + } + + /** Removes every upcoming item and returns them so the caller can offer Undo. */ + fun clearUpcoming(): List { + val removed = snapshot.upcoming + snapshot = snapshot.copy(upcoming = emptyList()) + shuffleBackup = null + return removed + } + + /** Restores a cleared tail in front of whatever was appended in the meantime. */ + fun restoreUpcoming(items: List): Snapshot { + snapshot = snapshot.copy(upcoming = items + snapshot.upcoming) + return snapshot + } + + private fun replayContext(): Snapshot { + val items = snapshot.context + if (items.isEmpty()) return snapshot.copy(current = null) + val first = items.first() + snapshot = snapshot.copy( + current = first, + upcoming = items.drop(1).let { if (snapshot.shuffle) it.shuffled(random) else it }, + history = snapshot.history + ) + shuffleBackup = if (snapshot.shuffle) items.drop(1) else null + return snapshot + } + + private companion object { + const val HISTORY_LIMIT = 20 + const val RESTART_THRESHOLD_MS = 3_000L + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt index 48ed6fa9..128622a1 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -27,10 +27,13 @@ class SidetracksScreenshotTest { current = current, upcoming = recent.drop(1), history = recent.drop(3), + recent = recent, + libraryItems = MockSpotifyDataSource.library(LibraryFilter.Albums), + searchResults = MockSpotifyDataSource.search("night", SearchScope.entries.toSet()), isPlaying = true, positionMs = 68_000L, savedTrackIds = setOf(current.id), - filter = LibraryFilter.Albums, + filter = LibraryFilter.Albums ) @Test fun home() = shoot("sidetracks_home") { @@ -41,7 +44,7 @@ class SidetracksScreenshotTest { onSearch = {}, onSettings = {}, onPlay = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -57,7 +60,7 @@ class SidetracksScreenshotTest { onArtist = {}, onShow = {}, onLoadMore = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -73,7 +76,7 @@ class SidetracksScreenshotTest { onQueue = {}, onNowPlaying = {}, onLoadMore = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -90,7 +93,7 @@ class SidetracksScreenshotTest { onAlbum = {}, onFollow = {}, onNowPlaying = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -106,7 +109,7 @@ class SidetracksScreenshotTest { onAlbum = {}, onPlaylist = {}, onShow = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -125,7 +128,7 @@ class SidetracksScreenshotTest { onAlbum = {}, onArtist = {}, onAddToPlaylist = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -140,7 +143,7 @@ class SidetracksScreenshotTest { onClearUpcoming = { emptyList() }, onRestoreUpcoming = {}, onShuffle = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -154,7 +157,7 @@ class SidetracksScreenshotTest { onClearHistory = {}, onSignOut = {}, onNotice = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } @@ -164,7 +167,7 @@ class SidetracksScreenshotTest { onBack = {}, onLogIn = {}, onCopyLink = {}, - onDismissNotice = {}, + onDismissNotice = {} ) } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt new file mode 100644 index 00000000..10e8133b --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt @@ -0,0 +1,60 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryEntry +import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class SpotifyHistoryRepositoryTest { + @Test fun `most recent item is first and duplicates move up`() { + val saved = mutableListOf>() + val history = SpotifyHistoryRepository(save = { saved += it }) + + history.record(track("a"), 1) + history.record(track("b"), 2) + history.record(track("a"), 3) + + assertEquals(listOf("a", "b"), history.items().map { it.id }) + assertEquals(3, saved.last().first().playedAtMs) + } + + @Test fun `history is bounded and drops the oldest entry`() { + val history = SpotifyHistoryRepository(limit = 3) + + listOf("a", "b", "c", "d").forEachIndexed { index, id -> history.record(track(id), index.toLong()) } + + assertEquals(listOf("d", "c", "b"), history.items().map { it.id }) + } + + @Test fun `clear empties memory and persists the empty list`() { + var saved: List? = null + val history = SpotifyHistoryRepository(save = { saved = it }) + history.record(track("a"), 1) + + history.clear() + + assertTrue(history.items().isEmpty()) + assertTrue(saved!!.isEmpty()) + } + + @Test fun `load seeds the repository in order`() { + val stored = listOf( + SpotifyHistoryEntry(track("a"), 2), + SpotifyHistoryEntry(track("b"), 1) + ) + val history = SpotifyHistoryRepository(load = { stored }) + + assertEquals(listOf("a", "b"), history.items().map { it.id }) + } + + private fun track(id: String) = SpotifyTrack( + id = id, + title = "Track $id", + artist = "Artist", + artistId = "artist", + albumId = "album", + artRes = 0, + durationMs = 180_000L + ) +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt new file mode 100644 index 00000000..2b0162cd --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt @@ -0,0 +1,157 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository +import kotlin.random.Random +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class SpotifyQueueRepositoryTest { + private val tracks = listOf( + track("a"), + track("b"), + track("c"), + track("d") + ) + + @Test fun `play from a row replaces the queue and records the previous item`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.play(tracks, 2) + + assertEquals("c", queue.snapshot.current?.id) + assertEquals(listOf("d"), queue.snapshot.upcoming.map { it.id }) + assertEquals(listOf("a"), queue.snapshot.history.map { it.id }) + } + + @Test fun `replaying the current item does not duplicate history`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 1) + queue.play(tracks, 1) + + assertEquals("b", queue.snapshot.current?.id) + assertTrue(queue.snapshot.history.isEmpty()) + } + + @Test fun `play next moves an existing upcoming item instead of duplicating it`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.playNext(tracks[2]) + + assertEquals(listOf("c", "b", "d"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `add to queue appends`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.addToQueue(track("z")) + + assertEquals(listOf("b", "c", "d", "z"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `next moves the current item into history`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + + queue.next(auto = false) + + assertEquals("b", queue.snapshot.current?.id) + assertEquals(listOf("a"), queue.snapshot.history.map { it.id }) + assertEquals(listOf("c", "d"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `previous restarts inside the threshold and rewinds outside it`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.next(auto = false) + assertEquals("b", queue.snapshot.current?.id) + + queue.previous(4_000L) + assertEquals("b", queue.snapshot.current?.id) + + queue.previous(1_000L) + assertEquals("a", queue.snapshot.current?.id) + assertEquals(listOf("b", "c", "d"), queue.snapshot.upcoming.map { it.id }) + assertTrue(queue.snapshot.history.isEmpty()) + } + + @Test fun `shuffle only reorders upcoming and restores it when switched off`() { + val queue = SpotifyQueueRepository(Random(7)) + queue.play(tracks, 0) + val original = queue.snapshot.upcoming.map { it.id } + + queue.toggleShuffle() + assertTrue(queue.snapshot.shuffle) + assertEquals("a", queue.snapshot.current?.id) + assertEquals(original.toSet(), queue.snapshot.upcoming.map { it.id }.toSet()) + + queue.toggleShuffle() + assertEquals(original, queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `repeat off stops at the end of the queue`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks.take(2), 0) + + queue.next(auto = true) + queue.next(auto = true) + + assertNull(queue.snapshot.current) + } + + @Test fun `repeat context replays the collection when the queue runs out`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks.take(2), 0) + queue.setRepeat(RepeatMode.Context) + + queue.next(auto = true) + queue.next(auto = true) + + assertEquals("a", queue.snapshot.current?.id) + assertEquals(listOf("b"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `repeat track leaves the current item in place`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.setRepeat(RepeatMode.Track) + + queue.next(auto = true) + + assertEquals("a", queue.snapshot.current?.id) + assertEquals(listOf("b", "c", "d"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `jump to an upcoming item moves the skipped items into history`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + + queue.jumpTo("c") + + assertEquals("c", queue.snapshot.current?.id) + assertEquals(listOf("a", "b"), queue.snapshot.history.map { it.id }) + assertEquals(listOf("d"), queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `clearing upcoming can be undone`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + + val removed = queue.clearUpcoming() + assertTrue(queue.snapshot.upcoming.isEmpty()) + + queue.restoreUpcoming(removed) + assertEquals(listOf("b", "c", "d"), queue.snapshot.upcoming.map { it.id }) + } + + private fun track(id: String) = SpotifyTrack( + id = id, + title = "Track $id", + artist = "Artist", + artistId = "artist", + albumId = "album", + artRes = 0, + durationMs = 180_000L + ) +} From b750e080b88cc205a0e80b9d1a294646ece89066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 21:08:43 +0100 Subject: [PATCH 10/46] Add encrypted Spotify auth and a process-wide session --- .../sidekit/apps/spotify/SpotifyGraph.kt | 33 +++- .../apps/spotify/SpotifySettingsScreens.kt | 6 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 40 ++++- .../auth/EncryptedSpotifyCredentialStore.kt | 49 ++++++ .../sidekit/apps/spotify/auth/SpotifyAuth.kt | 159 ++++++++++++++++++ .../apps/spotify/auth/SpotifyCredentials.kt | 39 +++++ .../sidekit/apps/spotify/auth/SpotifyPkce.kt | 85 ++++++++++ .../apps/spotify/auth/SpotifyTokenClient.kt | 95 +++++++++++ .../apps/spotify/bridge/NativeBridge.kt | 28 +++ .../apps/spotify/data/SpotifySession.kt | 52 ++++++ .../sidekit/apps/spotify/SpotifyAuthTest.kt | 140 +++++++++++++++ .../sidekit/apps/spotify/SpotifyPkceTest.kt | 61 +++++++ 12 files changed, 775 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyCredentials.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyPkce.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyTokenClient.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyAuthTest.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPkceTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 4578071f..28d2862f 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -1,10 +1,13 @@ package hrln.interactive.sidekit.apps.spotify +import android.content.Intent +import android.net.Uri import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.initializer @@ -16,6 +19,7 @@ import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.compose.navigation import androidx.navigation.navArgument +import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions import hrln.interactive.sidekit.design.components.SidekitScaffold import hrln.interactive.sidekit.navigation.LAUNCHER_ROUTE import hrln.interactive.sidekit.navigation.SubApp @@ -206,16 +210,18 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(SIGN_IN) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() - SpotifySignInScreen( - state, - onBack = { nav.popBackStack(LAUNCHER_ROUTE, inclusive = false) }, - onLogIn = { - vm.signIn() + LaunchedEffect(state.signedIn) { + if (state.signedIn) { nav.navigate(HOME) { popUpTo(HOME) { inclusive = false } launchSingleTop = true } - }, + } + } + SpotifySignInScreen( + state, + onBack = { nav.popBackStack(LAUNCHER_ROUTE, inclusive = false) }, + onLogIn = vm::signIn, onCopyLink = { vm.notice("Login link copied") }, onDismissNotice = vm::dismissNotice ) @@ -237,8 +243,21 @@ private fun MissingCollectionScreen(onBack: () -> Unit) { @Composable private fun NavBackStackEntry.spotifyViewModel(nav: NavHostController): SpotifyViewModel { val parent = remember(this) { nav.getBackStackEntry(ROOT) } + val context = LocalContext.current.applicationContext + val session = remember(context) { SpotifySessions.get(context) } + val openBrowser: suspend (String) -> Unit = remember(context) { + { url -> + context.startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + } + } return viewModel( parent, - factory = remember { viewModelFactory { initializer { SpotifyViewModel() } } } + factory = remember(session, openBrowser) { + viewModelFactory { + initializer { SpotifyViewModel(session = session, openBrowser = openBrowser) } + } + } ) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index adaaabd9..0aa11bfd 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -208,7 +208,11 @@ fun SpotifySignInScreen( SidekitItem( "Status", Modifier.listFade(listState, 2), - secondary = if (state.signedIn) "Connected" else "Waiting", + secondary = when { + state.authPending -> "Waiting for browser" + state.signedIn -> "Connected" + else -> "Waiting" + }, secondaryToEnd = true, colour = ItemColour.Muted ) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 135a0289..1f21f34b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -5,6 +5,8 @@ import androidx.lifecycle.viewModelScope import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository +import hrln.interactive.sidekit.apps.spotify.data.SpotifySession +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -22,6 +24,7 @@ data class SpotifyUiState( val libraryItems: List = emptyList(), val searchResults: SpotifySearchResults = SpotifySearchResults(), val searchPending: Boolean = false, + val authPending: Boolean = false, val isPlaying: Boolean = false, val positionMs: Long = 0L, val shuffle: Boolean = false, @@ -46,15 +49,22 @@ data class SpotifyUiState( */ class SpotifyViewModel( private val catalog: SpotifyCatalog = MockSpotifyDataSource, - private val history: SpotifyHistoryRepository = SpotifyHistoryRepository() + private val history: SpotifyHistoryRepository = SpotifyHistoryRepository(), + private val session: SpotifySession? = null, + private val openBrowser: (suspend (String) -> Unit)? = null ) : ViewModel() { - private val _state = MutableStateFlow(SpotifyUiState()) + private val _state = MutableStateFlow(SpotifyUiState(signedIn = session?.signedIn?.value ?: true)) val state: StateFlow = _state.asStateFlow() private val queue = SpotifyQueueRepository() private var searchJob: Job? = null init { + session?.let { + viewModelScope.launch { + it.signedIn.collect { signed -> _state.update { state -> state.copy(signedIn = signed) } } + } + } if (history.items().isEmpty()) { // Seed the reference account in the same order the fixture lists it: most recent first. catalog.recentlyPlayed().asReversed().forEach { history.record(it, 0L) } @@ -260,6 +270,7 @@ class SpotifyViewModel( } fun signOut() { + session?.signOut() queue.stop() _state.update { SpotifyUiState( @@ -273,8 +284,29 @@ class SpotifyViewModel( } fun signIn() { - _state.update { SpotifyUiState(signedIn = true) } - loadAccount() + val session = session + val openBrowser = openBrowser + if (session == null || openBrowser == null) { + // Screenshot and unit builds keep the local reference account. + _state.update { SpotifyUiState(signedIn = true) } + loadAccount() + return + } + if (_state.value.authPending) return + _state.update { it.copy(authPending = true, notice = null) } + viewModelScope.launch { + try { + session.signIn(openBrowser) + _state.update { it.copy(signedIn = true, authPending = false) } + loadAccount() + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy(authPending = false, notice = e.message ?: "Sign-in failed") + } + } + } } private fun loadAccount() { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt new file mode 100644 index 00000000..20d85a1c --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt @@ -0,0 +1,49 @@ +package hrln.interactive.sidekit.apps.spotify.auth + +import android.content.Context +import hrln.interactive.sidekit.apps.integrations.PrivateSettings +import org.json.JSONObject + +/** + * Token storage backed by Sidekit's [PrivateSettings]: an atomic app-private file encrypted with a + * non-exportable Android Keystore key. Tokens must never go into plain SharedPreferences. + */ +class EncryptedSpotifyCredentialStore( + context: Context, + name: String = "sidetracks_spotify" +) : SpotifyCredentialStore { + private val settings = PrivateSettings(context.applicationContext, name) + + override fun read(): SpotifyCredentials? { + val value = settings.read() + val access = value.optString(KEY_ACCESS).takeIf { it.isNotEmpty() } ?: return null + return SpotifyCredentials( + accessToken = access, + refreshToken = value.optString(KEY_REFRESH).takeIf { it.isNotEmpty() }, + expiresAtMs = value.optLong(KEY_EXPIRES_AT, 0L), + scope = value.optString(KEY_SCOPE), + tokenType = value.optString(KEY_TOKEN_TYPE, "Bearer") + ) + } + + override fun save(credentials: SpotifyCredentials) { + settings.write( + JSONObject() + .put(KEY_ACCESS, credentials.accessToken) + .put(KEY_REFRESH, credentials.refreshToken ?: "") + .put(KEY_EXPIRES_AT, credentials.expiresAtMs) + .put(KEY_SCOPE, credentials.scope) + .put(KEY_TOKEN_TYPE, credentials.tokenType) + ) + } + + override fun clear() = settings.clear() + + private companion object { + const val KEY_ACCESS = "access_token" + const val KEY_REFRESH = "refresh_token" + const val KEY_EXPIRES_AT = "expires_at_ms" + const val KEY_SCOPE = "scope" + const val KEY_TOKEN_TYPE = "token_type" + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt new file mode 100644 index 00000000..f828e3c7 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt @@ -0,0 +1,159 @@ +package hrln.interactive.sidekit.apps.spotify.auth + +import java.io.IOException +import java.net.InetAddress +import java.net.ServerSocket +import java.net.Socket +import java.net.SocketTimeoutException +import kotlin.coroutines.coroutineContext +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext + +/** + * Spotify's OAuth authorization-code flow with PKCE and a loopback callback. The app opens the + * system browser; the browser returns to a short-lived `127.0.0.1` listener, so there is no deep + * link and the user's password never enters Sidekit. + * + * The client id is Spotify's desktop client id, matching the one librespot presents at login5. + * Keep the two in step: Spotify rejects stored credentials minted by a different client. + */ +class SpotifyAuth( + private val store: SpotifyCredentialStore, + private val tokens: SpotifyTokenClient = HttpSpotifyTokenClient(), + private val nowMs: () -> Long = System::currentTimeMillis +) { + private val refreshLock = Mutex() + + fun hasCredentials(): Boolean = store.read() != null + + /** + * Runs the browser round trip and stores the resulting tokens. Throws with a user-facing + * message when Spotify denies the request, the state does not match, or the callback times out. + */ + suspend fun signIn(openBrowser: suspend (String) -> Unit): SpotifyCredentials { + val verifier = SpotifyPkce.verifier() + val state = SpotifyPkce.state() + + ServerSocket(0, 1, InetAddress.getByName("127.0.0.1")).use { server -> + server.soTimeout = CALLBACK_TIMEOUT_MS + val redirectUri = "http://127.0.0.1:${server.localPort}$REDIRECT_PATH" + val url = SpotifyPkce.authorizationUrl( + clientId = CLIENT_ID, + redirectUri = redirectUri, + challenge = SpotifyPkce.challenge(verifier), + state = state, + scopes = SCOPES + ) + openBrowser(url) + val code = awaitCode(server, state) + val credentials = tokens.exchange(CLIENT_ID, code, verifier, redirectUri) + store.save(credentials) + return credentials + } + } + + /** Returns a usable access token, refreshing it first when it is inside the expiry window. */ + suspend fun accessToken(): String? = refreshLock.withLock { + val credentials = store.read() ?: return@withLock null + if (!credentials.isExpired(nowMs())) return@withLock credentials.accessToken + val refreshToken = credentials.refreshToken ?: return@withLock null + runCatching { tokens.refresh(CLIENT_ID, refreshToken) } + .getOrNull() + ?.also(store::save) + ?.accessToken + } + + fun clear() = store.clear() + + private suspend fun awaitCode(server: ServerSocket, expectedState: String): String { + val closeOnCancel = coroutineContext[Job]?.invokeOnCompletion { + runCatching { server.close() } + } + try { + return withContext(Dispatchers.IO) { + while (true) { + val socket = try { + server.accept() + } catch (e: SocketTimeoutException) { + throw IOException("Sign-in timed out — please try again", e) + } catch (e: IOException) { + coroutineContext.ensureActive() + throw e + } + socket.use { + when (val result = readCallback(it, expectedState)) { + is SpotifyPkce.Callback.Code -> return@withContext result.code + is SpotifyPkce.Callback.Failure -> throw IOException(result.message) + SpotifyPkce.Callback.Ignored -> Unit + } + } + } + @Suppress("UNREACHABLE_CODE") + error("unreachable") + } + } catch (e: CancellationException) { + throw e + } finally { + closeOnCancel?.dispose() + } + } + + private fun readCallback(socket: Socket, expectedState: String): SpotifyPkce.Callback { + val requestLine = socket.getInputStream().bufferedReader().readLine() ?: return SpotifyPkce.Callback.Ignored + val target = requestLine.split(' ').getOrNull(1) ?: return SpotifyPkce.Callback.Ignored + val result = SpotifyPkce.parseCallback(target, expectedState) + val message = when (result) { + is SpotifyPkce.Callback.Code -> "Signed in to Sidetracks. You can close this page." + is SpotifyPkce.Callback.Failure -> "Sign-in did not complete. You can close this page." + SpotifyPkce.Callback.Ignored -> "Waiting for Spotify…" + } + respond(socket, message) + return result + } + + private fun respond(socket: Socket, message: String) { + val body = """ + + + Sidetracks + +

$message

+ """.trimIndent() + val bytes = body.toByteArray(Charsets.UTF_8) + val headers = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n" + + "Content-Length: ${bytes.size}\r\nConnection: close\r\n\r\n" + runCatching { + socket.getOutputStream().apply { + write(headers.toByteArray(Charsets.US_ASCII)) + write(bytes) + flush() + } + } + } + + private companion object { + /** + * Spotify's desktop ("keymaster") client id. This is the id librespot's default + * SessionConfig presents, and the loopback path is registered for it. + */ + const val CLIENT_ID = "65b708073fc0480ea92a077233ca87bd" + const val REDIRECT_PATH = "/login" + const val CALLBACK_TIMEOUT_MS = 5 * 60 * 1_000 + + const val SCOPES = + "app-remote-control playlist-modify playlist-modify-private " + + "playlist-modify-public playlist-read playlist-read-collaborative " + + "playlist-read-private streaming ugc-image-upload user-follow-modify " + + "user-follow-read user-library-modify user-library-read user-modify " + + "user-modify-playback-state user-modify-private user-personalized " + + "user-read-birthdate user-read-currently-playing user-read-email " + + "user-read-play-history user-read-playback-position " + + "user-read-playback-state user-read-private user-read-recently-played " + + "user-top-read" + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyCredentials.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyCredentials.kt new file mode 100644 index 00000000..b2fe69fb --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyCredentials.kt @@ -0,0 +1,39 @@ +package hrln.interactive.sidekit.apps.spotify.auth + +/** + * The minimum material needed to reconnect to Spotify later. The password never reaches the app; + * only the OAuth tokens are kept, encrypted by [SpotifyCredentialStore]. + */ +data class SpotifyCredentials( + val accessToken: String, + val refreshToken: String?, + val expiresAtMs: Long, + val scope: String, + val tokenType: String = "Bearer" +) { + /** Refresh a minute early so a request never races the expiry. */ + fun isExpired(nowMs: Long = System.currentTimeMillis()): Boolean = expiresAtMs <= nowMs + REFRESH_WINDOW_MS + + fun withTokenResponse( + accessToken: String, + refreshToken: String?, + expiresInSeconds: Long, + scope: String? + ): SpotifyCredentials = copy( + accessToken = accessToken, + // Spotify omits the refresh token when the grant did not rotate it. + refreshToken = refreshToken ?: this.refreshToken, + expiresAtMs = System.currentTimeMillis() + expiresInSeconds.coerceAtLeast(0L) * 1_000L, + scope = scope ?: this.scope + ) + + companion object { + const val REFRESH_WINDOW_MS = 60_000L + } +} + +interface SpotifyCredentialStore { + fun read(): SpotifyCredentials? + fun save(credentials: SpotifyCredentials) + fun clear() +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyPkce.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyPkce.kt new file mode 100644 index 00000000..f03bb663 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyPkce.kt @@ -0,0 +1,85 @@ +package hrln.interactive.sidekit.apps.spotify.auth + +import java.net.URLDecoder +import java.net.URLEncoder +import java.security.MessageDigest +import java.security.SecureRandom +import java.util.Base64 + +/** + * Pure OAuth PKCE helpers. Kept free of Android types so the security-sensitive parts can be + * verified by ordinary JVM tests. + */ +object SpotifyPkce { + private const val REDIRECT_PATH = "/login" + + fun verifier(random: SecureRandom = SecureRandom()): String { + val bytes = ByteArray(64) + random.nextBytes(bytes) + return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes) + } + + fun challenge(verifier: String): String { + val digest = MessageDigest.getInstance("SHA-256").digest(verifier.toByteArray(Charsets.US_ASCII)) + return Base64.getUrlEncoder().withoutPadding().encodeToString(digest) + } + + fun state(random: SecureRandom = SecureRandom()): String = verifier(random) + + fun authorizationUrl( + clientId: String, + redirectUri: String, + challenge: String, + state: String, + scopes: String + ): String = buildString { + append("https://accounts.spotify.com/authorize") + append("?client_id=").append(encode(clientId)) + append("&response_type=code") + append("&redirect_uri=").append(encode(redirectUri)) + append("&scope=").append(encode(scopes)) + append("&state=").append(encode(state)) + append("&code_challenge_method=S256") + append("&code_challenge=").append(encode(challenge)) + } + + fun isCallback(target: String): Boolean = target.substringBefore('?').substringBefore('#') == REDIRECT_PATH + + /** Parses the loopback request target (`/login?code=…`) after validating the CSRF state. */ + fun parseCallback(target: String, expectedState: String): Callback { + if (!isCallback(target)) return Callback.Ignored + val query = target.substringAfter('?', "").substringBefore('#') + val params = query.split('&') + .filter { it.isNotEmpty() } + .associate { + val key = decode(it.substringBefore('=')) + val value = decode(it.substringAfter('=', "")) + key to value + } + + params["error"]?.let { error -> + return Callback.Failure( + if (error == "access_denied") "Sign-in was cancelled" else "Spotify rejected the sign-in: $error" + ) + } + if (params["state"] != expectedState) { + return Callback.Failure("Sign-in could not be verified — please try again") + } + val code = params["code"].orEmpty() + return if (code.isEmpty()) { + Callback.Failure("Spotify did not return an authorization code") + } else { + Callback.Code(code) + } + } + + private fun encode(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name()) + + private fun decode(value: String): String = URLDecoder.decode(value, Charsets.UTF_8.name()) + + sealed interface Callback { + data class Code(val code: String) : Callback + data class Failure(val message: String) : Callback + data object Ignored : Callback + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyTokenClient.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyTokenClient.kt new file mode 100644 index 00000000..b689dd7d --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyTokenClient.kt @@ -0,0 +1,95 @@ +package hrln.interactive.sidekit.apps.spotify.auth + +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONObject + +/** Swappable so token parsing and refresh logic can be tested without the network. */ +interface SpotifyTokenClient { + suspend fun exchange( + clientId: String, + code: String, + verifier: String, + redirectUri: String + ): SpotifyCredentials + + suspend fun refresh(clientId: String, refreshToken: String): SpotifyCredentials +} + +/** + * Minimal form-encoded client for Spotify's token endpoint. Authorization-code exchange is the + * only place the PKCE verifier leaves the device; refresh tokens are rotated when Spotify returns + * a new one. + */ +class HttpSpotifyTokenClient : SpotifyTokenClient { + override suspend fun exchange( + clientId: String, + code: String, + verifier: String, + redirectUri: String + ): SpotifyCredentials = request( + mapOf( + "grant_type" to "authorization_code", + "code" to code, + "redirect_uri" to redirectUri, + "client_id" to clientId, + "code_verifier" to verifier + ), + previousRefresh = null + ) + + override suspend fun refresh(clientId: String, refreshToken: String): SpotifyCredentials = request( + mapOf( + "grant_type" to "refresh_token", + "refresh_token" to refreshToken, + "client_id" to clientId + ), + previousRefresh = refreshToken + ) + + private suspend fun request(form: Map, previousRefresh: String?): SpotifyCredentials = withContext(Dispatchers.IO) { + val body = form.entries.joinToString("&") { (key, value) -> "${encode(key)}=${encode(value)}" } + val connection = (URL(TOKEN_URL).openConnection() as HttpURLConnection).apply { + requestMethod = "POST" + connectTimeout = TIMEOUT_MS + readTimeout = TIMEOUT_MS + doOutput = true + setRequestProperty("Content-Type", "application/x-www-form-urlencoded") + setRequestProperty("Accept", "application/json") + } + try { + connection.outputStream.use { it.write(body.toByteArray(Charsets.UTF_8)) } + val status = connection.responseCode + val stream = if (status in 200..299) connection.inputStream else connection.errorStream + val text = stream?.bufferedReader()?.use { it.readText() }.orEmpty() + val json = runCatching { JSONObject(text) }.getOrElse { + throw IOException("Spotify returned an unreadable token response (HTTP $status)") + } + if (status !in 200..299) { + throw IOException(json.optString("error_description").ifEmpty { "Spotify token request failed (HTTP $status)" }) + } + val access = json.optString("access_token") + if (access.isEmpty()) throw IOException("Spotify did not return an access token") + val expiresIn = json.optLong("expires_in", 0L) + SpotifyCredentials( + accessToken = access, + refreshToken = json.optString("refresh_token").takeIf { it.isNotEmpty() } ?: previousRefresh, + expiresAtMs = System.currentTimeMillis() + expiresIn.coerceAtLeast(0L) * 1_000L, + scope = json.optString("scope"), + tokenType = json.optString("token_type", "Bearer") + ) + } finally { + connection.disconnect() + } + } + + private fun encode(value: String): String = java.net.URLEncoder.encode(value, Charsets.UTF_8.name()) + + private companion object { + const val TOKEN_URL = "https://accounts.spotify.com/api/token" + const val TIMEOUT_MS = 15_000 + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt new file mode 100644 index 00000000..ae354bcc --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -0,0 +1,28 @@ +package hrln.interactive.sidekit.apps.spotify.bridge + +import hrln.interactive.sidekit.system.NativeLibraries + +/** + * Guarded entry point to `libsidekit_spotify.so`. Never calls `System.loadLibrary` from an + * initializer: a build or emulator without the ABI is a normal state, not a crash. + * + * The native spike lands behind these declarations. Until then [isAvailable] is false and the + * Kotlin session can keep using the local catalog. + */ +object NativeBridge { + const val LIBRARY = "sidekit_spotify" + + val isAvailable: Boolean get() = NativeLibraries.load(LIBRARY) + + fun initialize(): Boolean = isAvailable && runCatching { nativeInit() }.getOrDefault(false) + + fun connect(accessToken: String): Boolean = isAvailable && runCatching { nativeConnect(accessToken) }.getOrDefault(false) + + fun disconnect() { + if (isAvailable) runCatching { nativeDisconnect() } + } + + private external fun nativeInit(): Boolean + private external fun nativeConnect(accessToken: String): Boolean + private external fun nativeDisconnect() +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt new file mode 100644 index 00000000..cff955ac --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -0,0 +1,52 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import android.content.Context +import hrln.interactive.sidekit.apps.spotify.auth.EncryptedSpotifyCredentialStore +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth +import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * Process-wide Spotify identity. It owns the credential store and token refresh, not the UI; the + * ViewModel observes [signedIn] and asks for an access token when a real gateway is connected. + */ +class SpotifySession( + private val auth: SpotifyAuth = SpotifyAuth(NoopCredentialStore) +) { + private val _signedIn = MutableStateFlow(auth.hasCredentials()) + val signedIn: StateFlow = _signedIn.asStateFlow() + + val nativeAvailable: Boolean get() = NativeBridge.isAvailable + + suspend fun signIn(openBrowser: suspend (String) -> Unit) { + auth.signIn(openBrowser) + _signedIn.value = true + } + + fun signOut() { + auth.clear() + _signedIn.value = false + } + + suspend fun accessToken(): String? = auth.accessToken() + + private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { + override fun read(): hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials? = null + override fun save(credentials: hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials) = Unit + override fun clear() = Unit + } +} + +/** Lazily creates the one session for the process, matching how Sidekit owns local playback. */ +object SpotifySessions { + @Volatile + private var instance: SpotifySession? = null + + fun get(context: Context): SpotifySession = instance ?: synchronized(this) { + instance ?: SpotifySession( + SpotifyAuth(EncryptedSpotifyCredentialStore(context.applicationContext)) + ).also { instance = it } + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyAuthTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyAuthTest.kt new file mode 100644 index 00000000..21d5ba7f --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyAuthTest.kt @@ -0,0 +1,140 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyTokenClient +import java.net.URI +import java.net.URL +import java.net.URLDecoder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test + +class SpotifyAuthTest { + private val now = 1_000_000L + + @Test fun `valid token is returned without refreshing`() = runTest { + val store = MemoryStore(credentials(expiresAt = now + 180_000L)) + val tokens = FakeTokens() + val auth = SpotifyAuth(store, tokens, nowMs = { now }) + + assertEquals("old-access", auth.accessToken()) + assertEquals(0, tokens.refreshCalls) + } + + @Test fun `expired token is refreshed and saved`() = runTest { + val store = MemoryStore(credentials(expiresAt = now + 10_000L)) + val tokens = FakeTokens(refreshed = credentials(access = "new-access", expiresAt = now + 3_600_000L)) + val auth = SpotifyAuth(store, tokens, nowMs = { now }) + + assertEquals("new-access", auth.accessToken()) + assertEquals(1, tokens.refreshCalls) + assertEquals("new-access", store.saved?.accessToken) + } + + @Test fun `refresh failure leaves the store untouched and returns null`() = runTest { + val original = credentials(expiresAt = now + 10_000L) + val store = MemoryStore(original) + val auth = SpotifyAuth(store, FakeTokens(refreshFails = true), nowMs = { now }) + + assertNull(auth.accessToken()) + assertEquals(original, store.saved) + } + + @Test fun `sign in completes against the loopback callback`() = runTest { + val store = MemoryStore(null) + val tokens = object : SpotifyTokenClient { + override suspend fun exchange( + clientId: String, + code: String, + verifier: String, + redirectUri: String + ): SpotifyCredentials { + assertEquals("test-code", code) + assertTrue(clientId.isNotEmpty()) + assertTrue(verifier.isNotEmpty()) + return credentials(access = "fresh-access") + } + + override suspend fun refresh(clientId: String, refreshToken: String): SpotifyCredentials = credentials(access = "refreshed") + } + val auth = SpotifyAuth(store, tokens) + val io = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + val result = auth.signIn { url -> + val params = URI(url).rawQuery.split('&').associate { + URLDecoder.decode(it.substringBefore('='), Charsets.UTF_8.name()) to + URLDecoder.decode(it.substringAfter('='), Charsets.UTF_8.name()) + } + val redirect = requireNotNull(params["redirect_uri"]) + val state = requireNotNull(params["state"]) + io.launch { + delay(50) + URL("$redirect?code=test-code&state=$state").openStream().use { it.readBytes() } + } + } + + assertEquals("fresh-access", result.accessToken) + assertEquals("fresh-access", store.saved?.accessToken) + } + + @Test fun `sign out clears credentials`() { + val store = MemoryStore(credentials()) + val session = hrln.interactive.sidekit.apps.spotify.data.SpotifySession(SpotifyAuth(store, FakeTokens())) + + session.signOut() + + assertNull(store.saved) + assertTrue(!session.signedIn.value) + } + + private fun credentials( + access: String = "old-access", + refresh: String? = "refresh", + expiresAt: Long = now + 180_000L + ) = SpotifyCredentials( + accessToken = access, + refreshToken = refresh, + expiresAtMs = expiresAt, + scope = "streaming" + ) + + private class MemoryStore(var saved: SpotifyCredentials?) : SpotifyCredentialStore { + override fun read(): SpotifyCredentials? = saved + override fun save(credentials: SpotifyCredentials) { + saved = credentials + } + + override fun clear() { + saved = null + } + } + + private class FakeTokens( + private val refreshed: SpotifyCredentials? = null, + private val refreshFails: Boolean = false + ) : SpotifyTokenClient { + var refreshCalls = 0 + + override suspend fun exchange( + clientId: String, + code: String, + verifier: String, + redirectUri: String + ): SpotifyCredentials = error("not used") + + override suspend fun refresh(clientId: String, refreshToken: String): SpotifyCredentials { + refreshCalls++ + if (refreshFails) error("refresh failed") + return requireNotNull(refreshed) + } + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPkceTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPkceTest.kt new file mode 100644 index 00000000..5256d790 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPkceTest.kt @@ -0,0 +1,61 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.auth.SpotifyPkce +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class SpotifyPkceTest { + @Test fun `challenge matches the RFC 7636 example`() { + assertEquals( + "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", + SpotifyPkce.challenge("dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk") + ) + } + + @Test fun `verifiers are URL safe and long enough`() { + val verifier = SpotifyPkce.verifier() + + assertTrue(verifier.length in 43..128) + assertTrue(verifier.all { it.isLetterOrDigit() || it == '-' || it == '_' }) + } + + @Test fun `authorization URL contains the PKCE fields`() { + val url = SpotifyPkce.authorizationUrl( + clientId = "client", + redirectUri = "http://127.0.0.1:4321/login", + challenge = "challenge", + state = "state", + scopes = "streaming user-read-private" + ) + + assertTrue(url.startsWith("https://accounts.spotify.com/authorize?")) + assertTrue(url.contains("code_challenge_method=S256")) + assertTrue(url.contains("code_challenge=challenge")) + assertTrue(url.contains("state=state")) + assertTrue(url.contains("scope=streaming+user-read-private")) + } + + @Test fun `callback accepts only the matching state`() { + assertEquals( + SpotifyPkce.Callback.Code("abc"), + SpotifyPkce.parseCallback("/login?code=abc&state=expected", "expected") + ) + assertTrue( + SpotifyPkce.parseCallback("/login?code=abc&state=wrong", "expected") is SpotifyPkce.Callback.Failure + ) + } + + @Test fun `denied callback becomes a readable failure`() { + assertEquals( + SpotifyPkce.Callback.Failure("Sign-in was cancelled"), + SpotifyPkce.parseCallback("/login?error=access_denied&state=expected", "expected") + ) + } + + @Test fun `favicon and other requests are ignored`() { + assertFalse(SpotifyPkce.isCallback("/favicon.ico")) + assertEquals(SpotifyPkce.Callback.Ignored, SpotifyPkce.parseCallback("/favicon.ico", "expected")) + } +} From 214143d9d047dcd4933764f796aae54c2e343871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 21:13:35 +0100 Subject: [PATCH 11/46] Add the playback gateway seam and session owner --- .../apps/spotify/data/SpotifyGateway.kt | 34 +++++ .../apps/spotify/playback/SpotifyPlayback.kt | 73 +++++++++++ .../apps/spotify/SpotifyPlaybackTest.kt | 117 ++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt new file mode 100644 index 00000000..2ed30c63 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt @@ -0,0 +1,34 @@ +package hrln.interactive.sidekit.apps.spotify.data + +/** + * Playback side of the Spotify connection. Catalog reads stay on [SpotifyCatalog]; this interface + * is deliberately transport-free so a fake can drive UI/playback tests and + * `NativeSpotifyGateway` can wrap JNI behind the same contract. + */ +interface SpotifyGateway { + suspend fun connect(accessToken: String) + suspend fun disconnect() + suspend fun load(uri: String, positionMs: Long, autoplay: Boolean) + suspend fun play() + suspend fun pause() + suspend fun stop() + suspend fun seek(positionMs: Long) + suspend fun setVolume(volume: Float) +} + +/** Stable gateway failures. Raw librespot strings must not reach the UI. */ +enum class SpotifyFailure { + Unavailable, + NotConnected, + AuthenticationExpired, + Network, + RateLimited, + TrackUnavailable, + Unknown +} + +class SpotifyGatewayException( + val failure: SpotifyFailure, + message: String, + cause: Throwable? = null +) : Exception(message, cause) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt new file mode 100644 index 00000000..58b7474f --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -0,0 +1,73 @@ +package hrln.interactive.sidekit.apps.spotify.playback + +import hrln.interactive.sidekit.apps.media.MediaSessionOwner +import hrln.interactive.sidekit.apps.media.MediaSessionRegistry +import hrln.interactive.sidekit.apps.media.MediaSessions +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGateway +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Process-scoped playback owner. The activity and screens may come and go; this object holds the + * native player lifetime and claims the one media session while it is producing audio. + * + * Main-thread only, like [MediaSessionOwner]. Long-running gateway work is moved onto [scope]. + */ +class SpotifyPlayback( + private val gateway: SpotifyGateway, + private val scope: CoroutineScope, + private val registry: MediaSessionRegistry = MediaSessions +) : MediaSessionOwner { + override val sessionId = "sidetracks" + override val notificationChannelId = "sidetracks" + override val notificationChannelName = "Sidetracks" + override val notificationId = 0x6D76 + + @Volatile + private var playing = false + + override val isActive: Boolean get() = playing + + /** Registers once so starting local Music can pause this owner even before the first track. */ + fun register() = registry.register(this) + + suspend fun start(uri: String, positionMs: Long) { + register() + registry.activate(this) + playing = true + try { + gateway.load(uri, positionMs, autoplay = true) + } catch (t: Throwable) { + playing = false + registry.deactivate(this) + throw t + } + } + + suspend fun resume() { + registry.activate(this) + playing = true + gateway.play() + } + + override fun pause() { + playing = false + registry.deactivate(this) + scope.launch { runCatching { gateway.pause() } } + } + + suspend fun seek(positionMs: Long) = gateway.seek(positionMs) + + suspend fun setVolume(volume: Float) = gateway.setVolume(volume) + + suspend fun stop() { + playing = false + registry.deactivate(this) + gateway.stop() + } + + suspend fun disconnect() { + stop() + gateway.disconnect() + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt new file mode 100644 index 00000000..a17f4c87 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt @@ -0,0 +1,117 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.media.MediaSessionOwner +import hrln.interactive.sidekit.apps.media.MediaSessionRegistry +import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGateway +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException +import hrln.interactive.sidekit.apps.spotify.playback.SpotifyPlayback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test + +class SpotifyPlaybackTest { + @Test fun `start claims the session and loads the track`() = runTest { + val gateway = FakeGateway() + val registry = MediaSessionRegistry() + val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) + + playback.start("spotify:track:a", 0L) + + assertSame(playback, registry.active) + assertTrue(playback.isActive) + assertEquals("spotify:track:a", gateway.loadedUri) + assertTrue(gateway.autoplay) + } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun `starting another owner pauses Sidetracks`() = runTest { + val gateway = FakeGateway() + val registry = MediaSessionRegistry() + val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) + val other = FakeOwner() + registry.register(other) + playback.start("spotify:track:a", 0L) + + registry.activate(other) + runCurrent() + + assertFalse(playback.isActive) + assertTrue(gateway.paused) + assertSame(other, registry.active) + } + + @Test fun `failed load releases the session`() = runTest { + val gateway = FakeGateway(failLoad = true) + val registry = MediaSessionRegistry() + val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) + + runCatching { playback.start("spotify:track:a", 0L) } + + assertFalse(playback.isActive) + assertNull(registry.active) + } + + @Test fun `stop deactivates and tells the gateway`() = runTest { + val gateway = FakeGateway() + val registry = MediaSessionRegistry() + val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) + playback.start("spotify:track:a", 0L) + + playback.stop() + + assertFalse(playback.isActive) + assertNull(registry.active) + assertTrue(gateway.stopped) + } + + private class FakeGateway(private val failLoad: Boolean = false) : SpotifyGateway { + var loadedUri: String? = null + var autoplay = false + var paused = false + var stopped = false + + override suspend fun connect(accessToken: String) = Unit + override suspend fun disconnect() = Unit + + override suspend fun load(uri: String, positionMs: Long, autoplay: Boolean) { + if (failLoad) throw SpotifyGatewayException(SpotifyFailure.TrackUnavailable, "missing") + loadedUri = uri + this.autoplay = autoplay + } + + override suspend fun play() = Unit + override suspend fun pause() { + paused = true + } + + override suspend fun stop() { + stopped = true + } + + override suspend fun seek(positionMs: Long) = Unit + override suspend fun setVolume(volume: Float) = Unit + } + + private class FakeOwner : MediaSessionOwner { + override val sessionId = "other" + override val notificationChannelId = "other" + override val notificationChannelName = "Other" + override val notificationId = 42 + override var isActive = true + private set + + override fun pause() { + isActive = false + } + } +} From 0305d013f37a807789199bae743001378ce7ead8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 21:14:02 +0100 Subject: [PATCH 12/46] Keep the existing scope when Spotify omits it on refresh --- .../hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt index f828e3c7..f54fe26d 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt @@ -64,6 +64,7 @@ class SpotifyAuth( val refreshToken = credentials.refreshToken ?: return@withLock null runCatching { tokens.refresh(CLIENT_ID, refreshToken) } .getOrNull() + ?.let { it.copy(scope = it.scope.ifEmpty { credentials.scope }) } ?.also(store::save) ?.accessToken } From b9e3dee222b2abff78e97db5640bad4f4b5fc4e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:18:35 +0100 Subject: [PATCH 13/46] Build the librespot native bridge for Android --- .gitignore | 3 + app/build.gradle.kts | 38 + native/sidekit-spotify/.cargo/config.toml | 6 + native/sidekit-spotify/Cargo.lock | 3538 +++++++++++++++++++++ native/sidekit-spotify/Cargo.toml | 28 + native/sidekit-spotify/LICENSE-librespot | 21 + native/sidekit-spotify/README.md | 38 + native/sidekit-spotify/src/audio.rs | 48 + native/sidekit-spotify/src/engine.rs | 264 ++ native/sidekit-spotify/src/lib.rs | 169 + 10 files changed, 4153 insertions(+) create mode 100644 native/sidekit-spotify/.cargo/config.toml create mode 100644 native/sidekit-spotify/Cargo.lock create mode 100644 native/sidekit-spotify/Cargo.toml create mode 100644 native/sidekit-spotify/LICENSE-librespot create mode 100644 native/sidekit-spotify/README.md create mode 100644 native/sidekit-spotify/src/audio.rs create mode 100644 native/sidekit-spotify/src/engine.rs create mode 100644 native/sidekit-spotify/src/lib.rs diff --git a/.gitignore b/.gitignore index c5b37965..84462928 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ build/ # Local clean-room reference (never committed) /.sidespot/ + +# Rust build output +native/**/target/ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0ddff4aa..a2072207 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -93,6 +93,7 @@ android { targetCompatibility = JavaVersion.VERSION_11 } packaging { resources.merges += setOf("META-INF/NOTICE.md", "META-INF/LICENSE.md", "META-INF/LICENSE.txt", "META-INF/NOTICE.txt") } + sourceSets["main"].jniLibs.directories.add(layout.buildDirectory.dir("generated/spotifyJniLibs").get().asFile.absolutePath) buildFeatures { compose = true } @@ -107,6 +108,43 @@ android { } } +// Sidetracks' librespot bridge. The generated ABI directory is a normal jniLibs source, so an +// unsupported ABI simply has no .so and the Kotlin bridge reports it as unavailable. +val spotifyJniLibs = layout.buildDirectory.dir("generated/spotifyJniLibs") +val cargo = listOf( + File(System.getProperty("user.home"), ".cargo/bin/cargo"), + File("/usr/bin/cargo"), + File("/usr/local/bin/cargo") +).firstOrNull { it.canExecute() } + +val buildSpotifyNative = tasks.register("buildSpotifyNative") { + description = "Build libsidekit_spotify.so for arm64-v8a and x86_64." + group = "native" + enabled = cargo != null + if (cargo == null) { + logger.warn("Sidetracks: cargo is unavailable; libsidekit_spotify.so will not be packaged.") + } + workingDir = rootProject.file("native/sidekit-spotify") + commandLine( + cargo?.absolutePath.orEmpty(), + "ndk", + "-t", "arm64-v8a", + "-t", "x86_64", + "-o", spotifyJniLibs.get().asFile.absolutePath, + "build", "--release" + ) + environment("ANDROID_NDK_HOME", androidComponents.sdkComponents.ndkDirectory.get().asFile.absolutePath) + inputs.dir(rootProject.file("native/sidekit-spotify/src")) + inputs.files(rootProject.file("native/sidekit-spotify/Cargo.toml"), rootProject.file("native/sidekit-spotify/Cargo.lock")) + outputs.dir(spotifyJniLibs) +} + +tasks.matching { + it.name.startsWith("merge") && (it.name.endsWith("NativeLibs") || it.name.endsWith("JniLibFolders")) +}.configureEach { + dependsOn(buildSpotifyNative) +} + dependencies { implementation(project(":sidekit")) implementation("com.sun.mail:android-mail:1.6.8") diff --git a/native/sidekit-spotify/.cargo/config.toml b/native/sidekit-spotify/.cargo/config.toml new file mode 100644 index 00000000..e32c5929 --- /dev/null +++ b/native/sidekit-spotify/.cargo/config.toml @@ -0,0 +1,6 @@ +# Android 15+ devices require 16 KB-aligned native library segments. +[target.aarch64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.x86_64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] diff --git a/native/sidekit-spotify/Cargo.lock b/native/sidekit-spotify/Cargo.lock new file mode 100644 index 00000000..630207c8 --- /dev/null +++ b/native/sidekit-spotify/Cargo.lock @@ -0,0 +1,3538 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures 0.2.17", +] + +[[package]] +name = "aho-corasick" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "async-trait" +version = "0.1.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" + +[[package]] +name = "cc" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ff2a8126fff0f8c839080d619675a585e61720bb12d2baceae45769fd66f155" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + +[[package]] +name = "chacha20" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link 0.2.1", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "combine" +version = "4.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4356eba6976435498d05a311ee9fc486da3bc81c200d3f0d7dd9703b41913f92" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core_detect" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f8f80099a98041a3d1622845c271458a2d73e688351bf3cb999266764b81d48" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f97d3416d5337d15c4b0d78c8c91df922ca143f57ec20e2d1c188333047ef04" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "displaydoc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "encoding_rs" +version = "0.8.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89253175e704ab869b04f9db224d38630e0211a53841a79181deae0889e169f5" +dependencies = [ + "cfg-if", + "core_detect", + "multiversion_no_op", + "rustversion", + "scopeguard", + "simdutf8", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00d174d5400e5e8fd687ad1049e2f578285fa914201b1af7e8b112a4546bd826" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c66915cc5391097de7a2b0b972e8505aa33c0df401fd3a6811dbdb32881ba48" + +[[package]] +name = "flate2" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626d4928326b646e70f65ed36fc7cf9f206bdb68a0019ee7c6b4709da4e084c4" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-io" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" + +[[package]] +name = "futures-sink" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-timer" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "governor" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9efcab3c1958580ff1f25a2a41be1668f7603d849bb63af523b208a3cc1223b8" +dependencies = [ + "cfg-if", + "futures-sink", + "futures-timer", + "futures-util", + "hashbrown 0.16.1", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "smallvec", + "spinning_top", + "web-time", +] + +[[package]] +name = "h2" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d29020232d6aa3fb1daca64c1127cf662cf97f254ae16c18c05b8ab635fc118" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "headers" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2700e3fe555c3310aa7286cac6167449f2c87e0eb58769c9208a1c58a1d106" +dependencies = [ + "base64 0.22.1", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3e324da4c95177d6291d4c8730197c0d1822f8a9766814a4a44fa5ab797c9c" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-proxy2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9043b7b23fb0bc4a1c7014c27b50a4fc42cc76206f71d34fc0dfe5b28ddc3faf" +dependencies = [ + "bytes", + "futures-util", + "headers", + "http", + "hyper", + "hyper-rustls 0.26.0", + "hyper-util", + "pin-project-lite", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", + "webpki", + "webpki-roots 0.26.11", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls 0.22.4", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.25.0", + "tower-service", + "webpki-roots 0.26.11", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa8e654703247911e29c23fbeaa261834bd9bb74efba2f9acddc37bfb127f53" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls 0.23.46", + "tokio", + "tokio-rustls 0.26.6", + "tower-service", + "webpki-roots 1.0.11", +] + +[[package]] +name = "hyper-util" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc03d96684f9226b8a787cdb71488417b53ab5ea8fdb1dac946cb9431cc8bff" +dependencies = [ + "base64 0.23.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe090a42a0d3140fed8c63263ed1fecc02422cb2f4bfe55cd8de2de1b8e105dd" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25d29b57c17c6528d0dc4c2141cfa2d08200465acb537a3909c8b9b8a4a984c0" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404a3b8c486f205c87fe7f23ab6894d84a4ad76d6e4671ae1981ba7def87c599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20f0088bfb7441f7a37fa0c9e20cb1e9cba1cac6b9ce8cd2a6dc44294f8e8cbc" + +[[package]] +name = "icu_properties" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ea83d991c56158f7cf898bf86436c1dfe7c07ee82fe88951f2695c9f37a379f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98f29af82d02b163fe50161388350e743bb3107cc8e8c2f7314cb505faddfcf" + +[[package]] +name = "icu_provider" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413099315636c588ee8f011190b329b971a09a8fae9d157e01dcae8379468514" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791930b43c0d5973160d90a8f3894509f2b273430f5c5c73b668636d0287c5c0" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "js-sys" +version = "0.3.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "librespot-audio" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3fe76acb49f58165484303edf0e7bd778f0e6d96f5c59e9d6b6fde1a90d36ff" +dependencies = [ + "aes", + "bytes", + "ctr", + "futures-util", + "http-body-util", + "hyper", + "hyper-util", + "librespot-core", + "log", + "tempfile", + "thiserror 2.0.21", + "tokio", +] + +[[package]] +name = "librespot-core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168bbe1c416980ddd9a969ebd6b50fb6c924eb1a3ded194285fa8ec0e2b1c68b" +dependencies = [ + "aes", + "base64 0.22.1", + "byteorder", + "bytes", + "data-encoding", + "flate2", + "form_urlencoded", + "futures-core", + "futures-util", + "governor", + "hmac", + "http", + "http-body-util", + "httparse", + "hyper", + "hyper-proxy2", + "hyper-rustls 0.27.10", + "hyper-util", + "librespot-oauth", + "librespot-protocol", + "log", + "nonzero_ext", + "num-bigint", + "num-derive", + "num-integer", + "num-traits", + "pbkdf2", + "pin-project-lite", + "priority-queue", + "protobuf", + "protobuf-json-mapping", + "quick-xml", + "rand 0.9.5", + "rand_distr", + "rsa", + "serde", + "serde_json", + "sha1", + "shannon", + "sysinfo", + "thiserror 2.0.21", + "time", + "tokio", + "tokio-stream", + "tokio-tungstenite", + "tokio-util", + "url", + "uuid", + "vergen-gitcl", +] + +[[package]] +name = "librespot-metadata" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9c688aa2acd3ed2498e31a95d6f2be49c0f18128db8958450ffd628aa88532" +dependencies = [ + "async-trait", + "bytes", + "librespot-core", + "librespot-protocol", + "log", + "protobuf", + "serde", + "serde_json", + "thiserror 2.0.21", + "uuid", +] + +[[package]] +name = "librespot-oauth" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686417d49c9d2c363392ffe28d6e469daca20a82dc414740930e078f5829661" +dependencies = [ + "log", + "oauth2", + "open", + "reqwest", + "thiserror 2.0.21", + "url", +] + +[[package]] +name = "librespot-playback" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88258620bf3e6808ea1fadd11639648d77c06280b9f5a4c9d14ea79f6f998af6" +dependencies = [ + "form_urlencoded", + "futures-util", + "librespot-audio", + "librespot-core", + "librespot-metadata", + "log", + "portable-atomic", + "rand 0.9.5", + "rand_distr", + "shell-words", + "symphonia", + "thiserror 2.0.21", + "tokio", + "zerocopy", +] + +[[package]] +name = "librespot-protocol" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e01f0b2d39f83fa162eb91d4a16313bcf99e77daf258abe8f7b7bcb1160b084" +dependencies = [ + "protobuf", + "protobuf-codegen", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" + +[[package]] +name = "lru-slab" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4050469837a6ff301cd14c1f8f24f88549e6d548f24f64e2148eb0f72cebc51f" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "mime" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5848f5a9e8117cf94f2f90faac24a87ff81600ff8628c1e3deaab572e76bfa1" + +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1788edb87fdc09c7e26304471e2f5be8cdefb1b6930d6e3985fc02ff53bf86ee" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multiversion_no_op" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743fb55ba31b18fb1ecef6bdc9aa2743314978ac084044301a7eee33fb99a20d" + +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + +[[package]] +name = "ntapi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.8", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_threads" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabcc654086861f9e1bc5115258de62ef92c79b20f4615a1b0cc57c4e82027d4" +dependencies = [ + "libc", +] + +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.17", + "http", + "rand 0.8.8", + "reqwest", + "serde", + "serde_json", + "serde_path_to_error", + "sha2", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "open" +version = "5.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95" +dependencies = [ + "is-wsl", + "libc", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "potential_utf" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "priority-queue" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93980406f12d9f8140ed5abe7155acb10bb1e69ea55c88960b9c2f117445ef96" +dependencies = [ + "equivalent", + "indexmap", + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "protobuf" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-codegen" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" +dependencies = [ + "anyhow", + "once_cell", + "protobuf", + "protobuf-parse", + "regex", + "tempfile", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-json-mapping" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d6e4be637b310d8a5c02fa195243328e2d97fa7df1127a27281ef1187fcb1d" +dependencies = [ + "protobuf", + "protobuf-support", + "thiserror 1.0.69", +] + +[[package]] +name = "protobuf-parse" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" +dependencies = [ + "anyhow", + "indexmap", + "log", + "protobuf", + "protobuf-support", + "tempfile", + "thiserror 1.0.69", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" +dependencies = [ + "thiserror 1.0.69", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quinn" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4051e23e9185c255a7e33ef59cdbca87a22d359052eecd22fc6b901fb37d9d11" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.46", + "socket2", + "thiserror 2.0.21", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e750cca55fe4f0439a15d0bb529da9651e79993e8e72c61a899a36d462befbe" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.3", + "rand_pcg", + "ring", + "rustc-hash", + "rustls 0.23.46", + "rustls-pki-types", + "slab", + "thiserror 2.0.21", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af66907df18639dcf4db56ca65490cabc4b27a97dbadd96f2926cca73298f016" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65c9fb96cbc91e3478eaae79a69fcd3f1ae4ad052e471fe6732fff548984b4af" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_distr" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" +dependencies = [ + "num-traits", + "rand 0.9.5", +] + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.2", +] + +[[package]] +name = "regex" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls 0.27.10", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.46", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls 0.26.6", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 1.0.11", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5e76742e5294153d4dde1207dbec5bdd81eef6fabdc0d2118babadabfd858aa" +dependencies = [ + "bitflags 2.13.2", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls" +version = "0.23.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e13bd8c0e9365c43cfa5c9e8f9ad49d3c8444926c9aac819e0e4dc503c8fdf" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki 0.103.16", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443b1b6d39d7c663139942cc4558262562d2c84123bb6282ef177009ca8b281" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cb23c424d8ee841bb4d813677ca237e2a610067d594fec130f87d6093495ab5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "shannon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" +dependencies = [ + "byteorder", +] + +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "sidekit-spotify" +version = "0.1.0" +dependencies = [ + "android_logger", + "jni", + "librespot-core", + "librespot-playback", + "log", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "149309498a5f7334b12ddae0cad8aba81ce785fe91755068867e017cf98fee46" + +[[package]] +name = "socket2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" + +[[package]] +name = "spinning_top" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf02766a1eb909bc7653557c66b384e44edc3ad979e6257c6173c65ea51bc7d" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-ogg", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901704edd0dfe137f1987838ee4f259e4e063c31371bdb423f7ae38ec6f77f02" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sysinfo" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252800745060e7b9ffb7b2badbd8b31cfa4aa2e61af879d0a3bf2a317c20217d" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix 1.1.6", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abc840c238154d58f9e86b2792261edfde565a17696e1cda1a449de27979801a" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "tokio" +version = "1.54.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc2678c2cdd569ef8215e2afd7954ada2ae20b4fdd2c5fe6139a3b02d105db" +dependencies = [ + "rustls 0.23.46", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "rustls 0.23.46", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.6", + "tungstenite", + "webpki-roots 0.26.11", +] + +[[package]] +name = "tokio-util" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464cf451ba96ebfc6f9b6542f17ee8b8956e33f1e40d9690624e59d7a7f8a4b" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "libc", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.2", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "rustls 0.23.46", + "rustls-pki-types", + "sha1", + "thiserror 2.0.21", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af3bd9d3e571c26fcce9d033b87ea5ab061b8d2ffe9b7a5675d810d68f03edf0" +dependencies = [ + "utf_types", +] + +[[package]] +name = "utf_types" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3b425286980a367278453f3b6d28cdcb134b574e5a35824a6ff5eebad50bb6" + +[[package]] +name = "uuid" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a06f08a856b009bc7bbaf7d3e79808595d032dd19074c0c2ef26e49e03e6bdc" +dependencies = [ + "getrandom 0.4.3", +] + +[[package]] +name = "vergen" +version = "9.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", + "time", + "vergen-lib", +] + +[[package]] +name = "vergen-gitcl" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9dfc1de6eb2e08a4ddf152f1b179529638bedc0ea95e6d667c014506377aefe" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", + "time", + "vergen", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4cdd0dd910afe868b7ef477227d8d538b46b3075031afee8a9f2acb0a2ed0b" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c" +dependencies = [ + "cfg-if", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14e0327cb5368ad60ccb22d04a9827a693c95a926d8d125e9fa39e0baec501" +dependencies = [ + "js-sys", + "tokio", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d179596ff5ec0f6de4db64d54011bde6289cc49603d3a6e4ce3f329207ab88" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.11", +] + +[[package]] +name = "webpki-roots" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" + +[[package]] +name = "yoke" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417ef3fcf6c300b7dbd25c85531a746931c02ad536f3cf4786ed1e88223340b3" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8ebde2db3681e8c9980cc27822030e68752690ddfa9473e739aeb4dbde6d71" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5fe1f8f1b06191a00962174c61aa5005e0bb391a6d80d07e24d115c01a92ed8" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863ad3ac83293fb4d740aedbfdc9240dd8d1a50c1099acd76ce80ce7c7230c7f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75b4683f6c7f45248d4d64056a24298c6281e0993356d7d1b4a1a962ef10d4a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" + +[[package]] +name = "zerotrie" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58a2799cfe9a1670c7ea8edbd52ef8edb93cb37a7f83bf31ba9e35d9dae6a40" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3743ddbf47738d929cd1ae2e199d40d0162979dfe31aef8ae646d2bf00bf97a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "zlib-rs" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b268e58e7c693d7c271f93ffc4ba3b380412554231c85bf61ca7af91042a4112" + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/native/sidekit-spotify/Cargo.toml b/native/sidekit-spotify/Cargo.toml new file mode 100644 index 00000000..4439a7e0 --- /dev/null +++ b/native/sidekit-spotify/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "sidekit-spotify" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "sidekit_spotify" +crate-type = ["cdylib"] + +[dependencies] +jni = "0.21" +librespot-core = { version = "0.8.0", default-features = false, features = ["rustls-tls-webpki-roots"] } +librespot-playback = { version = "0.8.0", default-features = false, features = ["rustls-tls-webpki-roots"] } +log = "0.4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["rt-multi-thread", "sync", "time"] } + +[target.'cfg(target_os = "android")'.dependencies] +android_logger = "0.14" + +[profile.release] +opt-level = "s" +lto = true +strip = true +codegen-units = 1 +panic = "abort" diff --git a/native/sidekit-spotify/LICENSE-librespot b/native/sidekit-spotify/LICENSE-librespot new file mode 100644 index 00000000..b06ecfb2 --- /dev/null +++ b/native/sidekit-spotify/LICENSE-librespot @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paul Lietar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/native/sidekit-spotify/README.md b/native/sidekit-spotify/README.md new file mode 100644 index 00000000..bbe396b0 --- /dev/null +++ b/native/sidekit-spotify/README.md @@ -0,0 +1,38 @@ +# libsidekit_spotify + +The Sidetracks native bridge. It links librespot 0.8.0 and exposes the JNI surface declared in +`apps/spotify/bridge/NativeBridge.kt`. + +## Toolchain + +- Rust stable. The first working build used 1.99.1. +- Android targets: `aarch64-linux-android`, `x86_64-linux-android`. +- `cargo-ndk` 4.1.2. +- Android NDK 27.1.12297006 (the version pinned by the app module). + +## Build + +Normally Gradle builds it as part of `:app:assembleDebug` / `:app:assembleRelease`: + +```sh +./gradlew :app:buildSpotifyNative +``` + +To run Cargo directly: + +```sh +export ANDROID_NDK_HOME="$HOME/Android/Sdk/ndk/27.1.12297006" +cargo ndk -t arm64-v8a -t x86_64 \ + -o ../../app/build/generated/spotifyJniLibs build --release +``` + +The output directory is a `jniLibs` source set. If Cargo or the NDK is unavailable, Gradle skips +the task and the APK has no library; `NativeLibraries.load("sidekit_spotify")` then returns false. + +## Dependency pins + +`Cargo.lock` pins librespot and the full graph. `vergen` is held at 9.0.6 because +`vergen-gitcl` 1.0.8 depends on `vergen-lib` 0.1.6 while `vergen` 9.1.0 pulled a second +incompatible `vergen-lib`. Do not update that crate without rebuilding Android targets. + +No librespot patches are applied. Upstream MIT licence: `LICENSE-librespot`. diff --git a/native/sidekit-spotify/src/audio.rs b/native/sidekit-spotify/src/audio.rs new file mode 100644 index 00000000..b8bedf76 --- /dev/null +++ b/native/sidekit-spotify/src/audio.rs @@ -0,0 +1,48 @@ +//! PCM sink. Decoded S16LE stereo is queued for the Kotlin AudioTrack drainer. +//! +//! The bounded channel is deliberate: when Kotlin stops draining, the librespot decode thread +//! blocks on `send` instead of racing ahead and allocating without limit. + +use librespot_playback::audio_backend::{Open, Sink, SinkResult}; +use librespot_playback::config::AudioFormat; +use librespot_playback::convert::Converter; +use librespot_playback::decoder::AudioPacket; +use std::sync::mpsc::SyncSender; + +pub const CHANNELS: u16 = 2; +pub const SAMPLE_RATE: u32 = 44_100; + +pub struct PcmSink { + tx: SyncSender>, +} + +impl PcmSink { + pub fn new(tx: SyncSender>) -> Self { + Self { tx } + } +} + +impl Open for PcmSink { + fn open(_device: Option, _format: AudioFormat) -> Self { + unreachable!("PcmSink is constructed with its channel") + } +} + +impl Sink for PcmSink { + fn write(&mut self, packet: AudioPacket, converter: &mut Converter) -> SinkResult<()> { + let pcm = match packet { + AudioPacket::Samples(samples) => { + let s16 = converter.f64_to_s16(&samples); + let mut bytes = Vec::with_capacity(s16.len() * 2); + for sample in s16 { + bytes.extend_from_slice(&sample.to_le_bytes()); + } + bytes + } + AudioPacket::Raw(bytes) => bytes, + }; + // A disconnected receiver means playback is shutting down; dropping is correct. + let _ = self.tx.send(pcm); + Ok(()) + } +} diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs new file mode 100644 index 00000000..b913de44 --- /dev/null +++ b/native/sidekit-spotify/src/engine.rs @@ -0,0 +1,264 @@ +//! One process-wide librespot session and player, guarded independently of the JNI callers. + +use crate::audio::{PcmSink, CHANNELS, SAMPLE_RATE}; +use librespot_core::authentication::Credentials; +use librespot_core::session::Session; +use librespot_core::SpotifyUri; +use librespot_playback::config::{Bitrate, PlayerConfig}; +use librespot_playback::mixer::VolumeGetter; +use librespot_playback::player::{Player, PlayerEventChannel}; +use std::sync::atomic::{AtomicU16, Ordering}; +use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; +use std::sync::{Arc, Mutex, OnceLock}; +use std::time::Duration; + +struct Volume(AtomicU16); + +struct SharedVolume(Arc); + +impl VolumeGetter for SharedVolume { + fn attenuation_factor(&self) -> f64 { + let volume = self.0 .0.load(Ordering::Relaxed).min(65_535) as f64 / 65_535.0; + volume * volume + } +} + +struct State { + session: Option, + player: Option>, + events: Option, + volume: Arc, +} + +impl Default for State { + fn default() -> Self { + Self { + session: None, + player: None, + events: None, + volume: Arc::new(Volume(AtomicU16::new(65_535))), + } + } +} + +static RUNTIME: OnceLock = OnceLock::new(); +static STATE: OnceLock> = OnceLock::new(); +static AUDIO_RX: OnceLock>>> = OnceLock::new(); + +fn runtime() -> &'static tokio::runtime::Runtime { + RUNTIME.get_or_init(|| { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .worker_threads(2) + .build() + .expect("tokio runtime") + }) +} + +fn state() -> &'static Mutex { + STATE.get_or_init(|| Mutex::new(State::default())) +} + +fn audio_rx() -> &'static Mutex>> { + AUDIO_RX.get_or_init(|| { + let (tx, rx) = sync_channel::>(64); + AUDIO_TX.set(tx).ok(); + Mutex::new(rx) + }) +} + +static AUDIO_TX: OnceLock>> = OnceLock::new(); + +pub fn init() -> Result<(), String> { + // Force the audio channel (and its sender) to exist before the player starts. + let _ = audio_rx(); + Ok(()) +} + +pub fn connect(access_token: &str) -> Result<(), String> { + disconnect(); + let rt = runtime(); + let _guard = rt.enter(); + let session = Session::new(Default::default(), None); + rt.block_on(session.connect(Credentials::with_access_token(access_token), false)) + .map_err(|e| format!("SESSION_CONNECT_FAILED: {e}"))?; + + let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + guard.session = Some(session); + Ok(()) +} + +pub fn disconnect() { + if let Ok(mut guard) = state().lock() { + if let Some(player) = guard.player.take() { + player.stop(); + } + guard.events = None; + if let Some(session) = guard.session.take() { + session.shutdown(); + } + } +} + +pub fn create_player() -> Result<(), String> { + let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + if guard.player.is_some() { + return Ok(()); + } + let session = guard + .session + .clone() + .ok_or_else(|| "SESSION_NOT_CONNECTED".to_string())?; + + let _ = audio_rx(); + let tx = AUDIO_TX + .get() + .ok_or_else(|| "AUDIO_UNINITIALISED".to_string())? + .clone(); + let volume = guard.volume.clone(); + let config = PlayerConfig { + bitrate: Bitrate::Bitrate320, + position_update_interval: Some(Duration::from_millis(500)), + ..PlayerConfig::default() + }; + let player = Player::new(config, session, Box::new(SharedVolume(volume)), move || { + Box::new(PcmSink::new(tx)) + }); + guard.events = Some(player.get_player_event_channel()); + guard.player = Some(player); + Ok(()) +} + +pub fn load(uri: &str, position_ms: u32, autoplay: bool) -> Result<(), String> { + let uri = SpotifyUri::from_uri(uri).map_err(|e| format!("INVALID_URI: {e}"))?; + if !uri.is_playable() { + return Err("NOT_PLAYABLE".to_string()); + } + let player = { + let guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + guard + .player + .clone() + .ok_or_else(|| "PLAYER_NOT_CREATED".to_string())? + }; + player.load(uri, autoplay, position_ms); + Ok(()) +} + +pub fn play() -> Result<(), String> { + player()?.play(); + Ok(()) +} + +pub fn pause() -> Result<(), String> { + player()?.pause(); + Ok(()) +} + +pub fn stop() -> Result<(), String> { + player()?.stop(); + Ok(()) +} + +pub fn seek(position_ms: u32) -> Result<(), String> { + player()?.seek(position_ms); + Ok(()) +} + +pub fn set_volume(volume: f32) { + let level = (volume.clamp(0.0, 1.0) * 65_535.0) as u16; + if let Ok(guard) = state().lock() { + guard.volume.0.store(level, Ordering::Relaxed); + } +} + +pub fn read_audio(timeout: Duration) -> Option> { + audio_rx().lock().ok()?.recv_timeout(timeout).ok() +} + +pub fn audio_format() -> String { + format!("{{\"format\":\"s16le\",\"sampleRate\":{SAMPLE_RATE},\"channels\":{CHANNELS}}}") +} + +pub fn poll_event() -> Option { + use librespot_playback::player::PlayerEvent; + use serde_json::json; + + let mut guard = state().lock().ok()?; + let events = guard.events.as_mut()?; + let event = events.try_recv().ok()?; + let value = match event { + PlayerEvent::Stopped { track_id, .. } => json!({"type": "stopped", "uri": uri(&track_id)}), + PlayerEvent::Loading { + track_id, + position_ms, + .. + } => { + json!({"type": "loading", "uri": uri(&track_id), "positionMs": position_ms}) + } + PlayerEvent::Playing { + track_id, + position_ms, + .. + } => { + json!({"type": "playing", "uri": uri(&track_id), "positionMs": position_ms}) + } + PlayerEvent::Paused { + track_id, + position_ms, + .. + } => { + json!({"type": "paused", "uri": uri(&track_id), "positionMs": position_ms}) + } + PlayerEvent::EndOfTrack { track_id, .. } => { + json!({"type": "ended", "uri": uri(&track_id)}) + } + PlayerEvent::Unavailable { track_id, .. } => { + json!({"type": "unavailable", "uri": uri(&track_id)}) + } + PlayerEvent::PositionChanged { position_ms, .. } + | PlayerEvent::PositionCorrection { position_ms, .. } + | PlayerEvent::Seeked { position_ms, .. } => { + json!({"type": "position", "positionMs": position_ms}) + } + PlayerEvent::VolumeChanged { volume } => json!({"type": "volume", "volume": volume}), + PlayerEvent::ShuffleChanged { shuffle } => json!({"type": "shuffle", "shuffle": shuffle}), + PlayerEvent::RepeatChanged { context, track } => { + json!({"type": "repeat", "context": context, "track": track}) + } + PlayerEvent::AutoPlayChanged { auto_play } => { + json!({"type": "autoplay", "autoplay": auto_play}) + } + _ => json!({"type": "other"}), + }; + Some(value.to_string()) +} + +fn uri(value: &SpotifyUri) -> String { + format!( + "spotify:{}:{}", + value.item_type(), + value.to_id().unwrap_or_default() + ) +} + +fn player() -> Result, String> { + let guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + guard + .player + .clone() + .ok_or_else(|| "PLAYER_NOT_CREATED".to_string()) +} + +#[cfg(test)] +mod tests { + use super::audio_format; + + #[test] + fn audio_format_is_interleaved_s16_stereo() { + let format = audio_format(); + assert!(format.contains("\"format\":\"s16le\"")); + assert!(format.contains("\"sampleRate\":44100")); + assert!(format.contains("\"channels\":2")); + } +} diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs new file mode 100644 index 00000000..f30b3e0e --- /dev/null +++ b/native/sidekit-spotify/src/lib.rs @@ -0,0 +1,169 @@ +//! JNI surface for Sidetracks. All state lives in `engine`; these functions only translate. + +mod audio; +mod engine; + +use jni::objects::{JObject, JString}; +use jni::sys::{jboolean, jbyteArray, jfloat, jint, jlong, JNI_FALSE, JNI_TRUE}; +use jni::JNIEnv; +use std::time::Duration; + +fn string(env: &mut JNIEnv, value: &JString) -> Option { + env.get_string(value).ok().map(Into::into) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeInit( + _env: JNIEnv, + _this: JObject, + _tmp_dir: JString, +) -> jboolean { + #[cfg(target_os = "android")] + android_logger::init_once( + android_logger::Config::default().with_max_level(log::LevelFilter::Info), + ); + if engine::init().is_ok() { + JNI_TRUE + } else { + JNI_FALSE + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeConnect( + mut env: JNIEnv, + _this: JObject, + token: JString, +) -> jboolean { + match string(&mut env, &token).map(|token| engine::connect(&token)) { + Some(Ok(())) => JNI_TRUE, + _ => JNI_FALSE, + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeDisconnect( + _env: JNIEnv, + _this: JObject, +) { + engine::disconnect(); +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCreatePlayer( + _env: JNIEnv, + _this: JObject, +) -> jboolean { + if engine::create_player().is_ok() { + JNI_TRUE + } else { + JNI_FALSE + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeLoad( + mut env: JNIEnv, + _this: JObject, + uri: JString, + position_ms: jint, + autoplay: jboolean, +) -> jboolean { + match string(&mut env, &uri) + .map(|uri| engine::load(&uri, position_ms.max(0) as u32, autoplay == JNI_TRUE)) + { + Some(Ok(())) => JNI_TRUE, + _ => JNI_FALSE, + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativePlay( + _env: JNIEnv, + _this: JObject, +) -> jboolean { + bool_result(engine::play()) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativePause( + _env: JNIEnv, + _this: JObject, +) -> jboolean { + bool_result(engine::pause()) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeStop( + _env: JNIEnv, + _this: JObject, +) -> jboolean { + bool_result(engine::stop()) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSeek( + _env: JNIEnv, + _this: JObject, + position_ms: jint, +) -> jboolean { + bool_result(engine::seek(position_ms.max(0) as u32)) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSetVolume( + _env: JNIEnv, + _this: JObject, + volume: jfloat, +) { + engine::set_volume(volume); +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeReadAudio( + env: JNIEnv, + _this: JObject, + timeout_ms: jlong, +) -> jbyteArray { + let timeout = Duration::from_millis(timeout_ms.max(0) as u64); + match engine::read_audio(timeout) { + Some(bytes) => match env.byte_array_from_slice(&bytes) { + Ok(array) => array.into_raw(), + Err(_) => std::ptr::null_mut(), + }, + None => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeAudioFormat( + env: JNIEnv, + _this: JObject, +) -> jni::sys::jstring { + match env.new_string(engine::audio_format()) { + Ok(value) => value.into_raw(), + Err(_) => std::ptr::null_mut(), + } +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativePollEvent( + env: JNIEnv, + _this: JObject, +) -> jni::sys::jstring { + match engine::poll_event() { + Some(event) => match env.new_string(event) { + Ok(value) => value.into_raw(), + Err(_) => std::ptr::null_mut(), + }, + None => std::ptr::null_mut(), + } +} + +fn bool_result(result: Result<(), String>) -> jboolean { + if result.is_ok() { + JNI_TRUE + } else { + JNI_FALSE + } +} From 9c3424279d46c3b4a8e2bb2c41af9c1e743cfc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:18:35 +0100 Subject: [PATCH 14/46] Expose librespot through the Spotify gateway --- .../apps/spotify/bridge/NativeBridge.kt | 45 +++++++++-- .../apps/spotify/bridge/SpotifyNative.kt | 22 ++++++ .../apps/spotify/data/NativeSpotifyGateway.kt | 70 +++++++++++++++++ .../apps/spotify/data/SpotifySession.kt | 13 ++++ .../apps/spotify/NativeSpotifyGatewayTest.kt | 75 +++++++++++++++++++ 5 files changed, 217 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index ae354bcc..250678ef 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -5,24 +5,53 @@ import hrln.interactive.sidekit.system.NativeLibraries /** * Guarded entry point to `libsidekit_spotify.so`. Never calls `System.loadLibrary` from an * initializer: a build or emulator without the ABI is a normal state, not a crash. - * - * The native spike lands behind these declarations. Until then [isAvailable] is false and the - * Kotlin session can keep using the local catalog. */ -object NativeBridge { +object NativeBridge : SpotifyNative { const val LIBRARY = "sidekit_spotify" - val isAvailable: Boolean get() = NativeLibraries.load(LIBRARY) + override val isAvailable: Boolean get() = NativeLibraries.load(LIBRARY) - fun initialize(): Boolean = isAvailable && runCatching { nativeInit() }.getOrDefault(false) + override fun initialize(): Boolean = isAvailable && runCatching { nativeInit() }.getOrDefault(false) - fun connect(accessToken: String): Boolean = isAvailable && runCatching { nativeConnect(accessToken) }.getOrDefault(false) + override fun connect(accessToken: String): Boolean = isAvailable && runCatching { nativeConnect(accessToken) }.getOrDefault(false) - fun disconnect() { + override fun disconnect() { if (isAvailable) runCatching { nativeDisconnect() } } + override fun createPlayer(): Boolean = isAvailable && runCatching { nativeCreatePlayer() }.getOrDefault(false) + + override fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean = isAvailable && runCatching { nativeLoad(uri, positionMs, autoplay) }.getOrDefault(false) + + override fun play(): Boolean = isAvailable && runCatching { nativePlay() }.getOrDefault(false) + + override fun pause(): Boolean = isAvailable && runCatching { nativePause() }.getOrDefault(false) + + override fun stop(): Boolean = isAvailable && runCatching { nativeStop() }.getOrDefault(false) + + override fun seek(positionMs: Long): Boolean = isAvailable && runCatching { nativeSeek(positionMs) }.getOrDefault(false) + + override fun setVolume(volume: Float) { + if (isAvailable) runCatching { nativeSetVolume(volume) } + } + + override fun readAudio(timeoutMs: Long): ByteArray? = if (isAvailable) runCatching { nativeReadAudio(timeoutMs) }.getOrNull() else null + + override fun audioFormat(): String? = if (isAvailable) runCatching { nativeAudioFormat() }.getOrNull() else null + + override fun pollEvent(): String? = if (isAvailable) runCatching { nativePollEvent() }.getOrNull() else null + private external fun nativeInit(): Boolean private external fun nativeConnect(accessToken: String): Boolean private external fun nativeDisconnect() + private external fun nativeCreatePlayer(): Boolean + private external fun nativeLoad(uri: String, positionMs: Long, autoplay: Boolean): Boolean + private external fun nativePlay(): Boolean + private external fun nativePause(): Boolean + private external fun nativeStop(): Boolean + private external fun nativeSeek(positionMs: Long): Boolean + private external fun nativeSetVolume(volume: Float) + private external fun nativeReadAudio(timeoutMs: Long): ByteArray? + private external fun nativeAudioFormat(): String? + private external fun nativePollEvent(): String? } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt new file mode 100644 index 00000000..903a234c --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -0,0 +1,22 @@ +package hrln.interactive.sidekit.apps.spotify.bridge + +/** + * Testable edge of the JNI library. Calls are blocking; `NativeSpotifyGateway` moves them to IO. + */ +interface SpotifyNative { + val isAvailable: Boolean + + fun initialize(): Boolean + fun connect(accessToken: String): Boolean + fun disconnect() + fun createPlayer(): Boolean + fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean + fun play(): Boolean + fun pause(): Boolean + fun stop(): Boolean + fun seek(positionMs: Long): Boolean + fun setVolume(volume: Float) + fun readAudio(timeoutMs: Long): ByteArray? + fun audioFormat(): String? + fun pollEvent(): String? +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt new file mode 100644 index 00000000..fa00ec7d --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt @@ -0,0 +1,70 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +/** + * [SpotifyGateway] backed by librespot. Every JNI call is blocking and therefore runs on IO. + * Native code returns only success/failure; stable failure mapping happens here. + */ +class NativeSpotifyGateway( + private val native: SpotifyNative, + private val accessToken: suspend () -> String? +) : SpotifyGateway { + override suspend fun connect(accessToken: String) = onIo { + requireNative() + if (!native.connect(accessToken)) fail(SpotifyFailure.AuthenticationExpired, "Could not connect") + if (!native.createPlayer()) fail(SpotifyFailure.Unknown, "Could not create the player") + } + + override suspend fun disconnect() = onIo { + if (native.isAvailable) native.disconnect() + } + + override suspend fun load(uri: String, positionMs: Long, autoplay: Boolean) = onIo { + requireNative() + if (!native.load(uri, positionMs, autoplay)) fail(SpotifyFailure.TrackUnavailable, "Track unavailable") + } + + override suspend fun play() = onIo { + requireNative() + if (!native.play()) fail(SpotifyFailure.NotConnected, "Playback is not ready") + } + + override suspend fun pause() = onIo { + requireNative() + if (!native.pause()) fail(SpotifyFailure.NotConnected, "Playback is not ready") + } + + override suspend fun stop() = onIo { + requireNative() + if (!native.stop()) fail(SpotifyFailure.NotConnected, "Playback is not ready") + } + + override suspend fun seek(positionMs: Long) = onIo { + requireNative() + if (!native.seek(positionMs)) fail(SpotifyFailure.NotConnected, "Playback is not ready") + } + + override suspend fun setVolume(volume: Float) = onIo { + requireNative() + native.setVolume(volume) + } + + /** Connects with the current stored token, refreshing it first when needed. */ + suspend fun ensureConnected() { + val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + connect(token) + } + + private fun requireNative() { + if (!native.isAvailable) fail(SpotifyFailure.Unavailable, "Spotify playback is unavailable in this build") + } + + private suspend fun onIo(block: suspend () -> Unit) { + withContext(Dispatchers.IO) { block() } + } + + private fun fail(failure: SpotifyFailure, message: String): Nothing = throw SpotifyGatewayException(failure, message) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index cff955ac..6270b242 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -20,16 +20,29 @@ class SpotifySession( val nativeAvailable: Boolean get() = NativeBridge.isAvailable + private val nativeGateway by lazy { + NativeSpotifyGateway(NativeBridge) { accessToken() } + } + val gateway: SpotifyGateway get() = nativeGateway + + init { + if (nativeAvailable) NativeBridge.initialize() + } + suspend fun signIn(openBrowser: suspend (String) -> Unit) { auth.signIn(openBrowser) _signedIn.value = true } fun signOut() { + NativeBridge.disconnect() auth.clear() _signedIn.value = false } + /** Connects librespot with the current access token, creating the one player on first use. */ + suspend fun ensurePlaybackConnected() = nativeGateway.ensureConnected() + suspend fun accessToken(): String? = auth.accessToken() private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt new file mode 100644 index 00000000..04e1340a --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -0,0 +1,75 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative +import hrln.interactive.sidekit.apps.spotify.data.NativeSpotifyGateway +import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class NativeSpotifyGatewayTest { + @Test fun `unavailable library maps to Unavailable`() = runTest { + val gateway = NativeSpotifyGateway(FakeNative(isAvailable = false)) { "token" } + + val failure = runCatching { gateway.connect("token") }.exceptionOrNull() + + assertTrue(failure is SpotifyGatewayException) + assertEquals(SpotifyFailure.Unavailable, (failure as SpotifyGatewayException).failure) + } + + @Test fun `connect creates the player`() = runTest { + val native = FakeNative() + val gateway = NativeSpotifyGateway(native) { "token" } + + gateway.connect("token") + + assertEquals("token", native.token) + assertTrue(native.playerCreated) + } + + @Test fun `failed load maps to TrackUnavailable`() = runTest { + val native = FakeNative().apply { loadOk = false } + val gateway = NativeSpotifyGateway(native) { "token" } + + val failure = runCatching { gateway.load("spotify:track:a", 0, true) }.exceptionOrNull() + + assertEquals(SpotifyFailure.TrackUnavailable, (failure as SpotifyGatewayException).failure) + } + + @Test fun `ensureConnected uses the stored token`() = runTest { + val native = FakeNative() + val gateway = NativeSpotifyGateway(native) { "stored" } + + gateway.ensureConnected() + + assertEquals("stored", native.token) + } + + private class FakeNative(override val isAvailable: Boolean = true) : SpotifyNative { + var token: String? = null + var playerCreated = false + var loadOk = true + + override fun initialize() = isAvailable + override fun connect(accessToken: String): Boolean { + token = accessToken + return isAvailable + } + override fun disconnect() = Unit + override fun createPlayer(): Boolean { + playerCreated = true + return isAvailable + } + override fun load(uri: String, positionMs: Long, autoplay: Boolean) = loadOk + override fun play() = isAvailable + override fun pause() = isAvailable + override fun stop() = isAvailable + override fun seek(positionMs: Long) = isAvailable + override fun setVolume(volume: Float) = Unit + override fun readAudio(timeoutMs: Long): ByteArray? = null + override fun audioFormat(): String? = null + override fun pollEvent(): String? = null + } +} From 1965bacae29929d0a8ef994b0b0aae38c4781318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:20:48 +0100 Subject: [PATCH 15/46] Drain librespot PCM into a streaming AudioTrack --- .../apps/spotify/data/SpotifySession.kt | 21 ++++ .../apps/spotify/playback/SpotifyAudioSink.kt | 117 ++++++++++++++++++ .../apps/spotify/playback/SpotifyPlayback.kt | 8 +- 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 6270b242..a26f7856 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -4,6 +4,11 @@ import android.content.Context import hrln.interactive.sidekit.apps.spotify.auth.EncryptedSpotifyCredentialStore import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge +import hrln.interactive.sidekit.apps.spotify.playback.SpotifyAudioSink +import hrln.interactive.sidekit.apps.spotify.playback.SpotifyPlayback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -25,6 +30,15 @@ class SpotifySession( } val gateway: SpotifyGateway get() = nativeGateway + private val playbackScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + val playback: SpotifyPlayback by lazy { + SpotifyPlayback( + gateway = nativeGateway, + scope = playbackScope, + audio = SpotifyAudioSink(NativeBridge, playbackScope) + ) + } + init { if (nativeAvailable) NativeBridge.initialize() } @@ -43,6 +57,13 @@ class SpotifySession( /** Connects librespot with the current access token, creating the one player on first use. */ suspend fun ensurePlaybackConnected() = nativeGateway.ensureConnected() + suspend fun play(uri: String, positionMs: Long = 0L) { + ensurePlaybackConnected() + playback.start(uri, positionMs) + } + + fun pause() = playback.pause() + suspend fun accessToken(): String? = auth.accessToken() private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt new file mode 100644 index 00000000..f9dc7287 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt @@ -0,0 +1,117 @@ +package hrln.interactive.sidekit.apps.spotify.playback + +import android.media.AudioAttributes +import android.media.AudioFormat +import android.media.AudioTrack +import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge +import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import org.json.JSONObject + +/** + * Drains PCM from librespot into a streaming [AudioTrack]. The native sink blocks when this + * consumer stops, so the decode thread cannot run away from the device. + */ +class SpotifyAudioSink( + private val native: SpotifyNative = NativeBridge, + private val scope: CoroutineScope, + private val onFailure: (Throwable) -> Unit = {} +) { + private var track: AudioTrack? = null + private var drain: Job? = null + + val isRunning: Boolean get() = drain?.isActive == true + + fun start() { + if (drain?.isActive == true) return + val format = readFormat() + val output = createTrack(format) ?: return + track = output + output.play() + drain = scope.launch(Dispatchers.IO) { + try { + while (isActive) { + val pcm = native.readAudio(READ_TIMEOUT_MS) ?: continue + if (pcm.isEmpty()) continue + val written = output.write(pcm, 0, pcm.size, AudioTrack.WRITE_BLOCKING) + if (written < 0) throw IllegalStateException("AudioTrack write failed: $written") + } + } catch (t: Throwable) { + if (isActive) onFailure(t) + } finally { + runCatching { output.pause() } + runCatching { output.flush() } + runCatching { output.release() } + if (track === output) track = null + } + } + } + + fun pause() { + drain?.cancel() + drain = null + track?.let { + runCatching { it.pause() } + runCatching { it.flush() } + runCatching { it.release() } + } + track = null + } + + private fun readFormat(): Format { + val raw = native.audioFormat() ?: return Format() + return runCatching { + val json = JSONObject(raw) + Format( + sampleRate = json.optInt("sampleRate", 44_100), + channels = json.optInt("channels", 2) + ) + }.getOrDefault(Format()) + } + + private fun createTrack(format: Format): AudioTrack? { + val channelMask = if (format.channels == 1) { + AudioFormat.CHANNEL_OUT_MONO + } else { + AudioFormat.CHANNEL_OUT_STEREO + } + val minBuffer = AudioTrack.getMinBufferSize( + format.sampleRate, + channelMask, + AudioFormat.ENCODING_PCM_16BIT + ) + if (minBuffer <= 0) { + onFailure(IllegalStateException("AudioTrack rejected the PCM format")) + return null + } + return runCatching { + AudioTrack.Builder() + .setAudioAttributes( + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build() + ) + .setAudioFormat( + AudioFormat.Builder() + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setSampleRate(format.sampleRate) + .setChannelMask(channelMask) + .build() + ) + .setBufferSizeInBytes(minBuffer * 2) + .setTransferMode(AudioTrack.MODE_STREAM) + .build() + }.onFailure(onFailure).getOrNull() + } + + private data class Format(val sampleRate: Int = 44_100, val channels: Int = 2) + + private companion object { + const val READ_TIMEOUT_MS = 100L + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index 58b7474f..446eb610 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -16,7 +16,8 @@ import kotlinx.coroutines.launch class SpotifyPlayback( private val gateway: SpotifyGateway, private val scope: CoroutineScope, - private val registry: MediaSessionRegistry = MediaSessions + private val registry: MediaSessionRegistry = MediaSessions, + private val audio: SpotifyAudioSink? = null ) : MediaSessionOwner { override val sessionId = "sidetracks" override val notificationChannelId = "sidetracks" @@ -35,10 +36,12 @@ class SpotifyPlayback( register() registry.activate(this) playing = true + audio?.start() try { gateway.load(uri, positionMs, autoplay = true) } catch (t: Throwable) { playing = false + audio?.pause() registry.deactivate(this) throw t } @@ -47,11 +50,13 @@ class SpotifyPlayback( suspend fun resume() { registry.activate(this) playing = true + audio?.start() gateway.play() } override fun pause() { playing = false + audio?.pause() registry.deactivate(this) scope.launch { runCatching { gateway.pause() } } } @@ -62,6 +67,7 @@ class SpotifyPlayback( suspend fun stop() { playing = false + audio?.pause() registry.deactivate(this) gateway.stop() } From 634649b69402bb4d534acf5342deb6c42bba8815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:23:45 +0100 Subject: [PATCH 16/46] Match the JNI declarations for init, load and seek --- native/sidekit-spotify/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index f30b3e0e..ed5cc623 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -4,7 +4,7 @@ mod audio; mod engine; use jni::objects::{JObject, JString}; -use jni::sys::{jboolean, jbyteArray, jfloat, jint, jlong, JNI_FALSE, JNI_TRUE}; +use jni::sys::{jboolean, jbyteArray, jfloat, jlong, JNI_FALSE, JNI_TRUE}; use jni::JNIEnv; use std::time::Duration; @@ -16,7 +16,6 @@ fn string(env: &mut JNIEnv, value: &JString) -> Option { pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeInit( _env: JNIEnv, _this: JObject, - _tmp_dir: JString, ) -> jboolean { #[cfg(target_os = "android")] android_logger::init_once( @@ -66,7 +65,7 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB mut env: JNIEnv, _this: JObject, uri: JString, - position_ms: jint, + position_ms: jlong, autoplay: jboolean, ) -> jboolean { match string(&mut env, &uri) @@ -105,7 +104,7 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSeek( _env: JNIEnv, _this: JObject, - position_ms: jint, + position_ms: jlong, ) -> jboolean { bool_result(engine::seek(position_ms.max(0) as u32)) } From aa23246efe7b1c5e7922364af2bbfd29bb4e3d8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:23:56 +0100 Subject: [PATCH 17/46] Start Spotify playback on the main thread --- .../interactive/sidekit/apps/spotify/data/SpotifySession.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index a26f7856..44219fd3 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.withContext /** * Process-wide Spotify identity. It owns the credential store and token refresh, not the UI; the @@ -59,7 +60,7 @@ class SpotifySession( suspend fun play(uri: String, positionMs: Long = 0L) { ensurePlaybackConnected() - playback.start(uri, positionMs) + withContext(Dispatchers.Main.immediate) { playback.start(uri, positionMs) } } fun pause() = playback.pause() From 630d5ba08e0ea56e89b3845be6b38c8055d48df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Sun, 8 Nov 2026 22:41:02 +0100 Subject: [PATCH 18/46] Search Spotify and play real tracks --- .../sidekit/apps/spotify/SpotifyModels.kt | 9 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 83 +++++++++++++++-- .../apps/spotify/data/SpotifySession.kt | 11 +++ .../apps/spotify/data/SpotifyWebApi.kt | 90 +++++++++++++++++++ .../sidekit/apps/spotify/SpotifyWebApiTest.kt | 65 ++++++++++++++ 5 files changed, 250 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyWebApiTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt index 5a59f1dd..199caff6 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt @@ -22,6 +22,9 @@ sealed interface SpotifyMediaItem { val durationMs: Long val playable: Boolean val explicit: Boolean + + /** Canonical Spotify URI for the real catalog; null for the local mockup fixtures. */ + val uri: String? get() = null } data class SpotifyTrack( @@ -33,7 +36,8 @@ data class SpotifyTrack( override val artRes: Int, override val durationMs: Long, override val explicit: Boolean = false, - override val playable: Boolean = true + override val playable: Boolean = true, + override val uri: String? = null ) : SpotifyMediaItem { override val creator: String get() = artist } @@ -46,7 +50,8 @@ data class SpotifyEpisode( val published: String, override val artRes: Int, override val durationMs: Long, - val video: Boolean = false + val video: Boolean = false, + override val uri: String? = null ) : SpotifyMediaItem { override val creator: String get() = show override val artistId: String? get() = null diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 1f21f34b..cc1f2c54 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -3,6 +3,8 @@ package hrln.interactive.sidekit.apps.spotify import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog +import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifySession @@ -101,6 +103,7 @@ class SpotifyViewModel( recorded(item) applyQueue(snapshot, positionMs = 0L, playing = true) _state.update { it.copy(notice = null) } + startRemote(item, 0L) } fun playItem(item: SpotifyMediaItem) = play(listOf(item), 0) @@ -115,8 +118,21 @@ class SpotifyViewModel( if (at >= 0) play(recent, at) else playItem(track) } - fun togglePlayPause() = _state.update { - if (it.current == null) it else it.copy(isPlaying = !it.isPlaying) + fun togglePlayPause() { + val s = _state.value + val item = s.current ?: return + val play = !s.isPlaying + _state.update { it.copy(isPlaying = play) } + if (item.uri == null || session == null) return + viewModelScope.launch { + try { + if (play) session.resume() else session.pause() + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(isPlaying = false, notice = playbackNotice(e)) } + } + } } fun next() { @@ -125,6 +141,7 @@ class SpotifyViewModel( if (snapshot.current !== before) { snapshot.current?.let(::recorded) applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + snapshot.current?.let { startRemote(it, 0L) } } } @@ -133,20 +150,35 @@ class SpotifyViewModel( if (s.current == null) return if (s.positionMs > 3_000L) { _state.update { it.copy(positionMs = 0L) } + s.current?.uri?.let { uri -> + session?.let { session -> + viewModelScope.launch { runCatching { session.seek(0L) } } + } + } return } val snapshot = queue.previous(s.positionMs) if (snapshot.current !== s.current) { snapshot.current?.let(::recorded) applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + snapshot.current?.let { startRemote(it, 0L) } } else { _state.update { it.copy(positionMs = 0L) } + s.current?.uri?.let { uri -> + session?.let { session -> + viewModelScope.launch { runCatching { session.seek(0L) } } + } + } } } - fun seekTo(positionMs: Long) = _state.update { - val duration = it.current?.durationMs ?: 0L - it.copy(positionMs = positionMs.coerceIn(0L, duration)) + fun seekTo(positionMs: Long) { + val current = _state.value.current ?: return + val target = positionMs.coerceIn(0L, current.durationMs) + _state.update { it.copy(positionMs = target) } + if (current.uri != null && session != null) { + viewModelScope.launch { runCatching { session.seek(target) } } + } } fun toggleShuffle() = applyQueue(queue.toggleShuffle()) @@ -266,6 +298,7 @@ class SpotifyViewModel( if (snapshot.current !== s.current) { snapshot.current?.let(::recorded) applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + snapshot.current?.let { startRemote(it, 0L) } } } @@ -351,7 +384,18 @@ class SpotifyViewModel( searchJob = viewModelScope.launch { delay(debounceMs) val scopes = _state.value.searchScopes - val results = catalog.search(query, scopes) + val results = try { + if (session != null && session.signedIn.value) { + SpotifySearchResults(tracks = session.searchTracks(query)) + } else { + catalog.search(query, scopes) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(notice = playbackNotice(e)) } + SpotifySearchResults() + } _state.update { if (it.query == query && it.searchScopes == scopes) { it.copy(searchResults = results, searchPending = false) @@ -386,6 +430,7 @@ class SpotifyViewModel( val snapshot = queue.next(auto = true) snapshot.current?.let(::recorded) applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + snapshot.current?.let { startRemote(it, 0L) } return } if (!hasNext && !auto) return @@ -397,10 +442,36 @@ class SpotifyViewModel( if (snapshot.current !== current) { snapshot.current?.let(::recorded) applyQueue(snapshot, positionMs = 0L, playing = snapshot.current?.playable == true) + snapshot.current?.let { startRemote(it, 0L) } } else { _state.update { it.copy(isPlaying = false, positionMs = 0L) } } } + /** Starts a real Spotify item; mock fixtures have no URI and keep the local simulation. */ + private fun startRemote(item: SpotifyMediaItem, positionMs: Long) { + val uri = item.uri ?: return + val session = session ?: return + viewModelScope.launch { + try { + session.play(uri, positionMs) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(isPlaying = false, notice = playbackNotice(e)) } + } + } + } + + private fun playbackNotice(error: Exception): String = when ((error as? SpotifyGatewayException)?.failure) { + SpotifyFailure.Unavailable -> "Spotify playback is unavailable in this build" + SpotifyFailure.NotConnected -> "Playback is not ready — try again" + SpotifyFailure.AuthenticationExpired -> "Spotify session expired — sign in again" + SpotifyFailure.Network -> "Couldn't reach Spotify" + SpotifyFailure.RateLimited -> "Spotify is busy — try again" + SpotifyFailure.TrackUnavailable -> "This track isn't available" + SpotifyFailure.Unknown, null -> error.message ?: "Playback failed" + } + private fun collectionKey(collection: SpotifyCollection) = "${collection.type.route}:${collection.id}" } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 44219fd3..b63bb471 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -31,6 +31,8 @@ class SpotifySession( } val gateway: SpotifyGateway get() = nativeGateway + private val webApi by lazy { SpotifyWebApi { accessToken() } } + private val playbackScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) val playback: SpotifyPlayback by lazy { SpotifyPlayback( @@ -63,10 +65,19 @@ class SpotifySession( withContext(Dispatchers.Main.immediate) { playback.start(uri, positionMs) } } + suspend fun resume() { + ensurePlaybackConnected() + withContext(Dispatchers.Main.immediate) { playback.resume() } + } + + suspend fun seek(positionMs: Long) = playback.seek(positionMs) + fun pause() = playback.pause() suspend fun accessToken(): String? = auth.accessToken() + suspend fun searchTracks(query: String): List = webApi.searchTracks(query) + private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { override fun read(): hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials? = null override fun save(credentials: hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials) = Unit diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt new file mode 100644 index 00000000..440b1ab9 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt @@ -0,0 +1,90 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.R +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack +import java.io.IOException +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLEncoder +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONObject + +/** + * The Spotify Web API reads the app needs. It deliberately returns UI models, not raw JSON: the + * screens never learn an endpoint or a field name. + */ +class SpotifyWebApi(private val accessToken: suspend () -> String?) { + suspend fun searchTracks(query: String, limit: Int = 20): List { + val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + val encoded = URLEncoder.encode(query, Charsets.UTF_8.name()) + val json = get("$SEARCH?q=$encoded&type=track&limit=${limit.coerceIn(1, 50)}&market=from_token", token) + return parseSearchTracks(json) + } + + /** Visible for tests: maps Spotify's search envelope without touching the network. */ + internal fun parseSearchTracks(json: JSONObject): List { + val items = json.optJSONObject("tracks")?.optJSONArray("items") ?: return emptyList() + return buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.let(::track)?.let(::add) + } + } + } + + private fun track(json: JSONObject): SpotifyTrack? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val album = json.optJSONObject("album") + val artist = json.optJSONArray("artists")?.optJSONObject(0) + return SpotifyTrack( + id = id, + title = json.optString("name", "Unknown track"), + artist = artist?.optString("name").orEmpty().ifEmpty { "Unknown artist" }, + artistId = artist?.optString("id").orEmpty(), + albumId = album?.optString("id").orEmpty(), + artRes = R.drawable.cover_night_signals, + durationMs = json.optLong("duration_ms", 0L), + explicit = json.optBoolean("explicit", false), + playable = true, + uri = json.optString("uri").takeIf { it.isNotEmpty() } + ) + } + + private suspend fun get(url: String, token: String): JSONObject = withContext(Dispatchers.IO) { + val connection = (URL(url).openConnection() as HttpURLConnection).apply { + requestMethod = "GET" + connectTimeout = TIMEOUT_MS + readTimeout = TIMEOUT_MS + setRequestProperty("Authorization", "Bearer $token") + setRequestProperty("Accept", "application/json") + } + try { + val status = connection.responseCode + val text = (if (status in 200..299) connection.inputStream else connection.errorStream) + ?.bufferedReader()?.use { it.readText() }.orEmpty() + when { + status == 401 -> fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + + status == 429 -> fail(SpotifyFailure.RateLimited, "Spotify is busy — try again") + + status !in 200..299 -> fail(SpotifyFailure.Network, "Spotify returned HTTP $status") + + else -> runCatching { JSONObject(text) } + .getOrElse { fail(SpotifyFailure.Unknown, "Spotify returned an unreadable response") } + } + } catch (e: SpotifyGatewayException) { + throw e + } catch (e: IOException) { + throw SpotifyGatewayException(SpotifyFailure.Network, "Couldn't reach Spotify", e) + } finally { + connection.disconnect() + } + } + + private fun fail(failure: SpotifyFailure, message: String): Nothing = throw SpotifyGatewayException(failure, message) + + private companion object { + const val SEARCH = "https://api.spotify.com/v1/search" + const val TIMEOUT_MS = 15_000 + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyWebApiTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyWebApiTest.kt new file mode 100644 index 00000000..0673072f --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyWebApiTest.kt @@ -0,0 +1,65 @@ +package hrln.interactive.sidekit.apps.spotify + +import hrln.interactive.sidekit.apps.spotify.data.SpotifyWebApi +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SpotifyWebApiTest { + private val api = SpotifyWebApi { null } + + @Test + fun `maps a track search result to the player shape`() { + val track = api.parseSearchTracks(JSONObject(RESULT)).single() + + assertEquals("4uLU6hMCjMI75M1A2tKUQC", track.id) + assertEquals("Never Gonna Give You Up", track.title) + assertEquals("Rick Astley", track.artist) + assertEquals("0gxyHStUsqpMadRV0Di1Qt", track.artistId) + assertEquals("6XhjNHCyCDyyGJRM5mg40G", track.albumId) + assertEquals(213_573L, track.durationMs) + assertFalse(track.explicit) + assertEquals("spotify:track:4uLU6hMCjMI75M1A2tKUQC", track.uri) + assertTrue(track.playable) + } + + @Test + fun `an empty envelope yields no rows`() { + assertTrue(api.parseSearchTracks(JSONObject("""{"tracks":{"items":[]}}""")).isEmpty()) + assertTrue(api.parseSearchTracks(JSONObject("{}")).isEmpty()) + } + + @Test + fun `a row without an id is skipped`() { + val json = JSONObject( + """{"tracks":{"items":[{"name":"No id"},{"id":"abc","name":"Kept","uri":"spotify:track:abc"}]}}""" + ) + + assertEquals(listOf("abc"), api.parseSearchTracks(json).map { it.id }) + } + + private companion object { + const val RESULT = """ + { + "tracks": { + "items": [ + { + "id": "4uLU6hMCjMI75M1A2tKUQC", + "name": "Never Gonna Give You Up", + "duration_ms": 213573, + "explicit": false, + "uri": "spotify:track:4uLU6hMCjMI75M1A2tKUQC", + "artists": [{"id": "0gxyHStUsqpMadRV0Di1Qt", "name": "Rick Astley"}], + "album": {"id": "6XhjNHCyCDyyGJRM5mg40G"} + } + ] + } + } + """ + } +} From 01fb5ba9ac5425317bc4577788600c7970bbaa0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 09:47:56 +0100 Subject: [PATCH 19/46] feat(sidetracks): use authenticated catalog and harden playback - Read library, search, collections, artists and shows through librespot SpClient/Pathfinder instead of the rate-limited Web API. - Sync Now Playing with native playback events and refresh the native catalog token. - Fix remote-artwork OOM crashes and format podcast dates/playlist owners. - Document the vendored librespot-core patch, implementation status and remaining work. --- .../sidekit/apps/media/RemoteArtwork.kt | 53 +- .../sidekit/apps/spotify/SpotifyComponents.kt | 44 +- .../sidekit/apps/spotify/SpotifyGraph.kt | 34 +- .../apps/spotify/SpotifyLibraryScreens.kt | 45 +- .../sidekit/apps/spotify/SpotifyModels.kt | 39 +- .../apps/spotify/SpotifyNowPlayingScreens.kt | 5 +- .../apps/spotify/SpotifySearchScreen.kt | 8 +- .../apps/spotify/SpotifySettingsScreens.kt | 8 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 272 +++- .../apps/spotify/bridge/NativeBridge.kt | 26 + .../apps/spotify/bridge/SpotifyNative.kt | 9 + .../apps/spotify/data/NativeSpotifyCatalog.kt | 255 ++++ .../apps/spotify/data/NativeSpotifyGateway.kt | 54 +- .../apps/spotify/data/SpotifyCatalog.kt | 19 +- .../apps/spotify/data/SpotifySession.kt | 20 +- .../apps/spotify/data/SpotifyWebApi.kt | 333 ++++- .../apps/spotify/data/SpotifyWebCatalog.kt | 27 + .../apps/spotify/MockSpotifyDataSource.kt | 18 +- .../apps/spotify/NativeSpotifyGatewayTest.kt | 11 + .../apps/spotify/SidetracksScreenshotTest.kt | 16 +- native/sidekit-spotify/Cargo.lock | 21 +- native/sidekit-spotify/Cargo.toml | 10 + native/sidekit-spotify/src/catalog.rs | 1320 +++++++++++++++++ native/sidekit-spotify/src/engine.rs | 67 +- native/sidekit-spotify/src/lib.rs | 134 ++ native/vendor/librespot-core/Cargo.toml | 275 ++++ native/vendor/librespot-core/Cargo.toml.orig | 120 ++ native/vendor/librespot-core/LICENSE | 21 + native/vendor/librespot-core/build.rs | 31 + native/vendor/librespot-core/src/apresolve.rs | 153 ++ native/vendor/librespot-core/src/audio_key.rs | 110 ++ .../librespot-core/src/authentication.rs | 189 +++ native/vendor/librespot-core/src/cache.rs | 494 ++++++ native/vendor/librespot-core/src/cdn_url.rs | 259 ++++ native/vendor/librespot-core/src/channel.rs | 229 +++ native/vendor/librespot-core/src/component.rs | 40 + native/vendor/librespot-core/src/config.rs | 170 +++ .../librespot-core/src/connection/codec.rs | 111 ++ .../src/connection/handshake.rs | 270 ++++ .../librespot-core/src/connection/mod.rs | 194 +++ native/vendor/librespot-core/src/date.rs | 81 + .../librespot-core/src/dealer/manager.rs | 174 +++ .../vendor/librespot-core/src/dealer/maps.rs | 155 ++ .../vendor/librespot-core/src/dealer/mod.rs | 724 +++++++++ .../librespot-core/src/dealer/protocol.rs | 196 +++ .../src/dealer/protocol/request.rs | 217 +++ .../librespot-core/src/deserialize_with.rs | 90 ++ .../librespot-core/src/diffie_hellman.rs | 67 + native/vendor/librespot-core/src/error.rs | 507 +++++++ native/vendor/librespot-core/src/file_id.rs | 62 + .../vendor/librespot-core/src/http_client.rs | 304 ++++ native/vendor/librespot-core/src/lib.rs | 46 + native/vendor/librespot-core/src/login5.rs | 270 ++++ .../vendor/librespot-core/src/mercury/mod.rs | 306 ++++ .../librespot-core/src/mercury/sender.rs | 56 + .../librespot-core/src/mercury/types.rs | 101 ++ native/vendor/librespot-core/src/packet.rs | 41 + .../vendor/librespot-core/src/proxytunnel.rs | 52 + native/vendor/librespot-core/src/session.rs | 957 ++++++++++++ native/vendor/librespot-core/src/socket.rs | 34 + native/vendor/librespot-core/src/spclient.rs | 960 ++++++++++++ .../vendor/librespot-core/src/spotify_id.rs | 387 +++++ .../vendor/librespot-core/src/spotify_uri.rs | 615 ++++++++ native/vendor/librespot-core/src/token.rs | 143 ++ native/vendor/librespot-core/src/util.rs | 175 +++ native/vendor/librespot-core/src/version.rs | 53 + native/vendor/librespot-core/tests/connect.rs | 21 + 67 files changed, 12119 insertions(+), 189 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt rename app/src/{main => test}/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt (93%) create mode 100644 native/sidekit-spotify/src/catalog.rs create mode 100644 native/vendor/librespot-core/Cargo.toml create mode 100644 native/vendor/librespot-core/Cargo.toml.orig create mode 100644 native/vendor/librespot-core/LICENSE create mode 100644 native/vendor/librespot-core/build.rs create mode 100644 native/vendor/librespot-core/src/apresolve.rs create mode 100644 native/vendor/librespot-core/src/audio_key.rs create mode 100644 native/vendor/librespot-core/src/authentication.rs create mode 100644 native/vendor/librespot-core/src/cache.rs create mode 100644 native/vendor/librespot-core/src/cdn_url.rs create mode 100644 native/vendor/librespot-core/src/channel.rs create mode 100644 native/vendor/librespot-core/src/component.rs create mode 100644 native/vendor/librespot-core/src/config.rs create mode 100644 native/vendor/librespot-core/src/connection/codec.rs create mode 100644 native/vendor/librespot-core/src/connection/handshake.rs create mode 100644 native/vendor/librespot-core/src/connection/mod.rs create mode 100644 native/vendor/librespot-core/src/date.rs create mode 100644 native/vendor/librespot-core/src/dealer/manager.rs create mode 100644 native/vendor/librespot-core/src/dealer/maps.rs create mode 100644 native/vendor/librespot-core/src/dealer/mod.rs create mode 100644 native/vendor/librespot-core/src/dealer/protocol.rs create mode 100644 native/vendor/librespot-core/src/dealer/protocol/request.rs create mode 100644 native/vendor/librespot-core/src/deserialize_with.rs create mode 100644 native/vendor/librespot-core/src/diffie_hellman.rs create mode 100644 native/vendor/librespot-core/src/error.rs create mode 100644 native/vendor/librespot-core/src/file_id.rs create mode 100644 native/vendor/librespot-core/src/http_client.rs create mode 100644 native/vendor/librespot-core/src/lib.rs create mode 100644 native/vendor/librespot-core/src/login5.rs create mode 100644 native/vendor/librespot-core/src/mercury/mod.rs create mode 100644 native/vendor/librespot-core/src/mercury/sender.rs create mode 100644 native/vendor/librespot-core/src/mercury/types.rs create mode 100644 native/vendor/librespot-core/src/packet.rs create mode 100644 native/vendor/librespot-core/src/proxytunnel.rs create mode 100644 native/vendor/librespot-core/src/session.rs create mode 100644 native/vendor/librespot-core/src/socket.rs create mode 100644 native/vendor/librespot-core/src/spclient.rs create mode 100644 native/vendor/librespot-core/src/spotify_id.rs create mode 100644 native/vendor/librespot-core/src/spotify_uri.rs create mode 100644 native/vendor/librespot-core/src/token.rs create mode 100644 native/vendor/librespot-core/src/util.rs create mode 100644 native/vendor/librespot-core/src/version.rs create mode 100644 native/vendor/librespot-core/tests/connect.rs diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt b/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt index 5d541565..6ac641b4 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/media/RemoteArtwork.kt @@ -6,6 +6,8 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.util.LruCache import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -23,6 +25,7 @@ import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp import androidx.core.util.AtomicFile import hrln.interactive.sidekit.design.components.SidekitIconImage import hrln.interactive.sidekit.design.icons.SidekitIcon @@ -111,9 +114,18 @@ object RemoteArtwork { } private fun readDisk(context: Context, url: String, w: Int, h: Int): Bitmap? = try { - BitmapFactory.decodeFile(File(cacheDir(context), fileName(url, w, h)).absolutePath) + val file = File(cacheDir(context), fileName(url, w, h)) + val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true } + BitmapFactory.decodeFile(file.absolutePath, bounds) + val options = BitmapFactory.Options().apply { + inPreferredConfig = Bitmap.Config.RGB_565 + inSampleSize = sampleSize(bounds.outWidth, bounds.outHeight, w, h) + } + BitmapFactory.decodeFile(file.absolutePath, options) } catch (_: Exception) { null + } catch (_: OutOfMemoryError) { + null } private suspend fun download(context: Context, url: String, w: Int, h: Int): Bitmap? { @@ -122,11 +134,21 @@ object RemoteArtwork { BitmapFactory.decodeByteArray(bytes, 0, bytes.size, bounds) if (bounds.outWidth <= 0 || bounds.outHeight <= 0) return null val options = BitmapFactory.Options().apply { + inPreferredConfig = Bitmap.Config.RGB_565 inSampleSize = sampleSize(bounds.outWidth, bounds.outHeight, w, h) } - val decoded = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options) ?: return null + val decoded = try { + BitmapFactory.decodeByteArray(bytes, 0, bytes.size, options) + } catch (_: OutOfMemoryError) { + null + } ?: return null // cropTo recycles the source; store the cropped result so the next restart decodes it directly. - val bitmap = Thumbnails.cropTo(decoded, w, h) + val bitmap = try { + Thumbnails.cropTo(decoded, w, h) + } catch (_: OutOfMemoryError) { + decoded.recycle() + return null + } writeDisk(context, url, w, h, bitmap) return bitmap } @@ -207,10 +229,29 @@ object RemoteArtwork { } /** - * Draws [url] filling this box, with a Sidekit icon until it arrives (or if it never does). - * Requests cancel when the composable leaves composition, and a hit in the memory cache paints - * without a recomposition of the row. + * Draws [url] filling the space this composable is given, with a Sidekit icon until it arrives + * (or if it never does). Requests cancel when the composable leaves composition, and a hit in the + * memory cache paints without a recomposition of the row. */ +@Composable +fun RemoteArtworkImage( + url: String?, + modifier: Modifier = Modifier, + placeholder: SidekitIcon = SidekitIcon.Album, + contentDescription: String? = null +) { + BoxWithConstraints(modifier) { + RemoteArtworkImage( + url = url, + width = maxWidth.value.coerceIn(1f, 1024f).dp, + height = maxHeight.value.coerceIn(1f, 1024f).dp, + modifier = Modifier.fillMaxSize(), + placeholder = placeholder, + contentDescription = contentDescription + ) + } +} + @Composable fun RemoteArtworkImage( url: String?, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index f9aff9b6..d9d34749 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -50,6 +50,7 @@ import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import hrln.interactive.sidekit.apps.media.RemoteArtworkImage import hrln.interactive.sidekit.apps.media.formatDuration import hrln.interactive.sidekit.design.action.ActionMotion import hrln.interactive.sidekit.design.action.ActionTone @@ -63,14 +64,15 @@ import hrln.interactive.sidekit.design.components.SidekitTile import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme -/** Square remote-style artwork, backed here by the mockup's bundled covers. */ +/** Square artwork: remote when the catalog has a URL, otherwise a local fixture or icon. */ @Composable fun SpotifyCover( @DrawableRes artRes: Int, modifier: Modifier = Modifier, shape: androidx.compose.ui.graphics.Shape = SidekitTheme.shapes.control, bordered: Boolean = false, - contentDescription: String? = null + contentDescription: String? = null, + artUrl: String? = null ) { val colors = SidekitTheme.colors Box( @@ -79,12 +81,27 @@ fun SpotifyCover( .background(colors.surfaceA2) .then(if (bordered) Modifier.border(1.dp, colors.light, shape) else Modifier) ) { - Image( - painter = painterResource(artRes), - contentDescription = contentDescription, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) + when { + artUrl != null -> RemoteArtworkImage( + artUrl, + Modifier.fillMaxSize(), + placeholder = SidekitIcon.Album, + contentDescription = contentDescription + ) + + artRes != 0 -> Image( + painter = painterResource(artRes), + contentDescription = contentDescription, + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) + + else -> SidekitIconImage( + SidekitIcon.Album, + Modifier.align(Alignment.Center), + contentDescription = contentDescription + ) + } } } @@ -120,7 +137,7 @@ fun SpotifyMediaRow( horizontalArrangement = Arrangement.spacedBy(13.dp) ) { if (showArt) { - SpotifyCover(item.artRes, Modifier.size(68.dp)) + SpotifyCover(item.artRes, Modifier.size(68.dp), artUrl = item.artUrl) } else if (index != null) { Box(Modifier.size(30.dp), contentAlignment = Alignment.Center) { SidekitText("${index + 1}", style = SidekitTheme.typography.label, color = colors.surfaceA6) @@ -199,7 +216,7 @@ fun NowPlayingTile( SidekitIconImage(SidekitIcon.MusicNote2, size = 36.dp) } } else { - SpotifyCover(current.artRes, Modifier.size(72.dp)) + SpotifyCover(current.artRes, Modifier.size(72.dp), artUrl = current.artUrl) } Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(1.dp)) { SidekitText( @@ -224,9 +241,9 @@ fun LibraryTile(onClick: () -> Unit, modifier: Modifier = Modifier) { } @Composable -fun ArtworkTile(@DrawableRes artRes: Int, label: String, modifier: Modifier = Modifier) { +fun ArtworkTile(@DrawableRes artRes: Int, label: String, modifier: Modifier = Modifier, artUrl: String? = null) { SidekitTile(modifier) { - SpotifyCover(artRes, Modifier.fillMaxSize(), shape = SidekitTheme.shapes.item, contentDescription = label) + SpotifyCover(artRes, Modifier.fillMaxSize(), shape = SidekitTheme.shapes.item, contentDescription = label, artUrl = artUrl) } } @@ -361,6 +378,7 @@ fun SpotifyLibraryRow( title: String, secondary: String, modifier: Modifier = Modifier, + artUrl: String? = null, onClick: () -> Unit ) { val colors = SidekitTheme.colors @@ -370,7 +388,7 @@ fun SpotifyLibraryRow( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(13.dp) ) { - SpotifyCover(artRes, Modifier.size(68.dp)) + SpotifyCover(artRes, Modifier.size(68.dp), artUrl = artUrl) Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(2.dp)) { SidekitText(title, style = SidekitTheme.typography.bodySmall, maxLines = 1) SidekitText(secondary, style = SidekitTheme.typography.caption, color = colors.surfaceA6, maxLines = 1) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 28d2862f..74011220 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -19,6 +19,7 @@ import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.compose.navigation import androidx.navigation.navArgument +import hrln.interactive.sidekit.apps.media.RemoteArtwork import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions import hrln.interactive.sidekit.design.components.SidekitScaffold import hrln.interactive.sidekit.navigation.LAUNCHER_ROUTE @@ -59,6 +60,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onLibrary = { nav.navigate(LIBRARY) }, onSearch = { nav.navigate(SEARCH) }, onSettings = { nav.navigate(SETTINGS) }, + onRefresh = vm::refresh, onPlay = vm::playLibraryTrack, onDismissNotice = vm::dismissNotice ) @@ -76,7 +78,6 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onArtist = { nav.navigate(artistRoute(it.id)) }, onShow = { nav.navigate(collectionRoute(SpotifyCollectionType.Show, it.id)) }, - onLoadMore = { vm.notice("Everything in this filter is already loaded") }, onDismissNotice = vm::dismissNotice ) } @@ -85,9 +86,10 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { val state by vm.state.collectAsStateWithLifecycle() val type = SpotifyCollectionType.entries.firstOrNull { it.route == entry.arguments?.getString("type") } val id = entry.arguments?.getString("id").orEmpty() + LaunchedEffect(type, id) { type?.let { vm.ensureCollection(it, id) } } val collection = type?.let { vm.collection(it, id) } if (collection == null) { - MissingCollectionScreen(onBack = { nav.popBackStack() }) + MissingCollectionScreen(onBack = { nav.popBackStack() }, loading = type?.let { "${it.route}:$id" in state.pendingCollections } == true) } else { CollectionScreen( collection, @@ -101,7 +103,6 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { }, onQueue = { collection.items.firstOrNull()?.let(vm::addToQueue) }, onNowPlaying = { nav.navigate(NOW_PLAYING) }, - onLoadMore = { vm.notice("Everything in this collection is already loaded") }, onDismissNotice = vm::dismissNotice ) } @@ -109,9 +110,11 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(ARTIST, arguments = artistArgs) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() - val artist = vm.artist(entry.arguments?.getString("id").orEmpty()) + val artistId = entry.arguments?.getString("id").orEmpty() + LaunchedEffect(artistId) { vm.ensureArtist(artistId) } + val artist = vm.artist(artistId) if (artist == null) { - MissingCollectionScreen(onBack = { nav.popBackStack() }) + MissingCollectionScreen(onBack = { nav.popBackStack() }, loading = artistId in state.pendingArtists) } else { val albums = vm.albumsBy(artist) val tracks = vm.topTracks(artist) @@ -169,7 +172,6 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onArtist = { state.current?.artistId?.let { nav.navigate(artistRoute(it)) } }, - onAddToPlaylist = vm::addCurrentToPlaylist, onDismissNotice = vm::dismissNotice ) } @@ -222,7 +224,6 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { state, onBack = { nav.popBackStack(LAUNCHER_ROUTE, inclusive = false) }, onLogIn = vm::signIn, - onCopyLink = { vm.notice("Login link copied") }, onDismissNotice = vm::dismissNotice ) } @@ -230,12 +231,16 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { } @Composable -private fun MissingCollectionScreen(onBack: () -> Unit) { +private fun MissingCollectionScreen(onBack: () -> Unit, loading: Boolean = false) { SidekitScaffold(title = "Sidetracks", actions = spotifyBack("Back", onBack)) { padding -> androidx.compose.foundation.layout.Box( androidx.compose.ui.Modifier.padding(padding) ) { - EmptyMessage("This collection isn't available", "Go back and choose another") + if (loading) { + EmptyMessage("Loading…", "Fetching from Spotify") + } else { + EmptyMessage("This collection isn't available", "Go back and choose another") + } } } } @@ -254,9 +259,16 @@ private fun NavBackStackEntry.spotifyViewModel(nav: NavHostController): SpotifyV } return viewModel( parent, - factory = remember(session, openBrowser) { + factory = remember(session, openBrowser, context) { viewModelFactory { - initializer { SpotifyViewModel(session = session, openBrowser = openBrowser) } + initializer { + SpotifyViewModel( + catalog = session.catalog, + session = session, + openBrowser = openBrowser, + clearArtworkCache = { RemoteArtwork.clear(context) } + ) + } } } ) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index d4864884..1eef5147 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -36,6 +36,7 @@ fun SidetracksHomeScreen( onLibrary: () -> Unit, onSearch: () -> Unit, onSettings: () -> Unit, + onRefresh: () -> Unit, onPlay: (SpotifyTrack) -> Unit, onDismissNotice: () -> Unit ) { @@ -51,7 +52,7 @@ fun SidetracksHomeScreen( artKey = state.current?.id, art = { if (state.current != null) { - SpotifyCover(state.current.artRes, Modifier.height(SidekitTheme.spacing.tileIconSize)) + SpotifyCover(state.current.artRes, Modifier.height(SidekitTheme.spacing.tileIconSize), artUrl = state.current.artUrl) } else { hrln.interactive.sidekit.design.components.SidekitIconImage(SidekitIcon.MusicNote2) } @@ -64,7 +65,10 @@ fun SidetracksHomeScreen( SidekitScaffold( title = "Sidetracks", actions = bar, - options = listOf(SideAction("settings", "Settings", SidekitIcon.Settings, onClick = onSettings)) + options = listOf( + SideAction("refresh", "Refresh", SidekitIcon.Undo, onClick = onRefresh), + SideAction("settings", "Settings", SidekitIcon.Settings, onClick = onSettings) + ) ) { padding -> val listState = rememberLazyListState() SidekitList(padding, state = listState) { @@ -102,7 +106,6 @@ fun LibraryScreen( onAlbum: (SpotifyAlbum) -> Unit, onArtist: (SpotifyArtist) -> Unit, onShow: (SpotifyShow) -> Unit, - onLoadMore: () -> Unit, onDismissNotice: () -> Unit ) { val colors = SidekitTheme.colors @@ -146,28 +149,13 @@ fun LibraryScreen( val modifier = Modifier.listFade(listState, index).then(if (index == 0) Modifier.focusRequester(first) else Modifier) when (item) { is SpotifyTrack -> SpotifyMediaRow(item, modifier, secondary = item.artist, onClick = { onTrack(item) }) - is SpotifyPlaylist -> SpotifyLibraryRow(item.artRes, item.name, "${item.owner} · ${item.tracks.size} tracks", modifier) { onPlaylist(item) } - is SpotifyAlbum -> SpotifyLibraryRow(item.artRes, item.title, "${item.artist} · ${item.year}", modifier) { onAlbum(item) } - is SpotifyArtist -> SpotifyLibraryRow(item.artRes, item.name, "${item.albumIds.size} albums", modifier) { onArtist(item) } - is SpotifyShow -> SpotifyLibraryRow(item.artRes, item.name, item.publisher, modifier) { onShow(item) } + is SpotifyPlaylist -> SpotifyLibraryRow(item.artRes, item.name, "${item.owner} · ${item.trackCount} tracks", modifier, artUrl = item.artUrl) { onPlaylist(item) } + is SpotifyAlbum -> SpotifyLibraryRow(item.artRes, item.title, "${item.artist} · ${item.year}", modifier, artUrl = item.artUrl) { onAlbum(item) } + is SpotifyArtist -> SpotifyLibraryRow(item.artRes, item.name, "Artist", modifier, artUrl = item.artUrl) { onArtist(item) } + is SpotifyShow -> SpotifyLibraryRow(item.artRes, item.name, item.publisher, modifier, artUrl = item.artUrl) { onShow(item) } else -> Unit } } - if (items.isNotEmpty()) { - item(key = "more") { - TerminalRow( - "Load more", - secondary = when (state.filter) { - LibraryFilter.LikedSongs -> "${items.size} liked songs" - LibraryFilter.Playlists -> "${items.size} playlists" - LibraryFilter.Albums -> "${items.size} albums" - LibraryFilter.Artists -> "${items.size} artists" - LibraryFilter.Shows -> "${items.size} shows" - }, - onClick = onLoadMore - ) - } - } } } } @@ -183,7 +171,6 @@ fun CollectionScreen( onArtist: () -> Unit, onQueue: () -> Unit, onNowPlaying: () -> Unit, - onLoadMore: () -> Unit, onDismissNotice: () -> Unit ) { val colors = SidekitTheme.colors @@ -210,7 +197,7 @@ fun CollectionScreen( SidekitList(padding, state = listState) { item(key = "tiles") { SidekitItemRow(Modifier.listFade(listState, 0)) { - ArtworkTile(collection.artRes, "${collection.title} artwork", Modifier.weight(1f)) + ArtworkTile(collection.artRes, "${collection.title} artwork", Modifier.weight(1f), collection.artUrl) PlayTile(onPlay, Modifier.weight(1f).focusRequester(playFocus)) SpotifyIconTile( if (saved) SidekitIcon.Check else SidekitIcon.Bookmark, @@ -238,11 +225,10 @@ fun CollectionScreen( Modifier.listFade(listState, index + 2), index = index, showArt = false, - secondary = if (item is SpotifyEpisode) item.published else item.creator, + secondary = if (item is SpotifyEpisode) formatPublishedDate(item.published) else item.creator, onClick = { onItem(index) } ) } - item(key = "more") { TerminalRow("Load more", onClick = onLoadMore) } } } } @@ -276,7 +262,7 @@ fun ArtistScreen( item(key = "tiles") { SidekitItemRow(Modifier.listFade(listState, 0)) { PlayTile(onPlay, Modifier.weight(1f)) - ArtworkTile(artist.artRes, "${artist.name} artwork", Modifier.weight(1f)) + ArtworkTile(artist.artRes, "${artist.name} artwork", Modifier.weight(1f), artist.artUrl) } } item(key = "topLabel") { SectionLabel("Top tracks") } @@ -288,8 +274,9 @@ fun ArtistScreen( SpotifyLibraryRow( album.artRes, album.title, - "${album.year} · ${album.tracks.size} tracks", - Modifier.listFade(listState, index + tracks.size + 3) + "${album.year}" + if (album.trackCount > 0) " · ${album.trackCount} tracks" else "", + Modifier.listFade(listState, index + tracks.size + 3), + artUrl = album.artUrl ) { onAlbum(album) } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt index 199caff6..8d944982 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt @@ -23,8 +23,11 @@ sealed interface SpotifyMediaItem { val playable: Boolean val explicit: Boolean - /** Canonical Spotify URI for the real catalog; null for the local mockup fixtures. */ + /** Canonical Spotify URI for the real catalog; null for the local test fixtures. */ val uri: String? get() = null + + /** Remote cover art for the real catalog; null for local fixtures and placeholder art. */ + val artUrl: String? get() = null } data class SpotifyTrack( @@ -37,7 +40,8 @@ data class SpotifyTrack( override val durationMs: Long, override val explicit: Boolean = false, override val playable: Boolean = true, - override val uri: String? = null + override val uri: String? = null, + override val artUrl: String? = null ) : SpotifyMediaItem { override val creator: String get() = artist } @@ -51,7 +55,8 @@ data class SpotifyEpisode( override val artRes: Int, override val durationMs: Long, val video: Boolean = false, - override val uri: String? = null + override val uri: String? = null, + override val artUrl: String? = null ) : SpotifyMediaItem { override val creator: String get() = show override val artistId: String? get() = null @@ -66,7 +71,9 @@ data class SpotifyAlbum( val artistId: String, val year: Int, @param:DrawableRes val artRes: Int, - val tracks: List + val tracks: List, + val artUrl: String? = null, + val trackCount: Int = tracks.size ) data class SpotifyPlaylist( @@ -75,7 +82,9 @@ data class SpotifyPlaylist( val owner: String, val ownerId: String?, @param:DrawableRes val artRes: Int, - val tracks: List + val tracks: List, + val artUrl: String? = null, + val trackCount: Int = tracks.size ) data class SpotifyShow( @@ -83,14 +92,18 @@ data class SpotifyShow( val name: String, val publisher: String, @param:DrawableRes val artRes: Int, - val episodes: List + val episodes: List, + val artUrl: String? = null, + val episodeCount: Int = episodes.size ) data class SpotifyArtist( val id: String, val name: String, @param:DrawableRes val artRes: Int, - val albumIds: List + val albumIds: List, + val artUrl: String? = null, + val albumCount: Int = albumIds.size ) /** One screen's worth of a collection, already resolved by the gateway. */ @@ -101,7 +114,8 @@ data class SpotifyCollection( val subtitle: String, val artRes: Int, val items: List, - val artistId: String? = null + val artistId: String? = null, + val artUrl: String? = null ) data class SpotifySearchResults( @@ -156,3 +170,12 @@ data class SpotifyQueueItem( val item: SpotifyMediaItem, val isCurrent: Boolean = false ) + +/** Spotify metadata dates are ISO calendar dates; show them the way Sidekit shows release dates. */ +fun formatPublishedDate(value: String): String { + val date = value.substringBefore('T').substringBefore(' ') + if (date.length != 10 || date[4] != '-' || date[7] != '-') return value + return runCatching { + java.time.LocalDate.parse(date).format(java.time.format.DateTimeFormatter.ofPattern("d MMM uuuu")) + }.getOrDefault(value) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 4d9028bc..5b7a41ba 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -54,7 +54,6 @@ fun SpotifyNowPlayingScreen( onQueue: () -> Unit, onAlbum: () -> Unit, onArtist: () -> Unit, - onAddToPlaylist: () -> Unit, onDismissNotice: () -> Unit ) { val current = state.current @@ -84,7 +83,6 @@ fun SpotifyNowPlayingScreen( add(SideAction("queue", "Queue", SidekitIcon.QueueMusic, onClick = onQueue)) if (current is SpotifyTrack) add(SideAction("album", "Album", SidekitIcon.Album, onClick = onAlbum)) if (current.artistId != null) add(SideAction("artist", "Artist", SidekitIcon.Artist, onClick = onArtist)) - add(SideAction("playlist", "Add to playlist", SidekitIcon.Add, onClick = onAddToPlaylist)) } } SidekitScaffold(title = null, actions = actions, options = options) { padding -> @@ -105,7 +103,8 @@ fun SpotifyNowPlayingScreen( Modifier.size(art), shape = SidekitTheme.shapes.item, bordered = true, - contentDescription = "${current.title} artwork" + contentDescription = "${current.title} artwork", + artUrl = current.artUrl ) } Column(horizontalAlignment = Alignment.CenterHorizontally) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 70cb5894..1b423d1f 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -117,28 +117,28 @@ fun SpotifySearchScreen( if (results.artists.isNotEmpty()) { item(key = "artistsLabel") { SectionLabel("Artists") } itemsIndexed(results.artists, key = { _, artist -> "r${artist.id}" }) { index, artist -> - SpotifyLibraryRow(artist.artRes, artist.name, "${artist.albumIds.size} albums", Modifier.listFade(listState, slot + index)) { onArtist(artist) } + SpotifyLibraryRow(artist.artRes, artist.name, "Artist", Modifier.listFade(listState, slot + index), artUrl = artist.artUrl) { onArtist(artist) } } slot += results.artists.size } if (results.albums.isNotEmpty()) { item(key = "albumsLabel") { SectionLabel("Albums") } itemsIndexed(results.albums, key = { _, album -> "a${album.id}" }) { index, album -> - SpotifyLibraryRow(album.artRes, album.title, "${album.artist} · ${album.year}", Modifier.listFade(listState, slot + index)) { onAlbum(album) } + SpotifyLibraryRow(album.artRes, album.title, "${album.artist} · ${album.year}", Modifier.listFade(listState, slot + index), artUrl = album.artUrl) { onAlbum(album) } } slot += results.albums.size } if (results.playlists.isNotEmpty()) { item(key = "playlistsLabel") { SectionLabel("Playlists") } itemsIndexed(results.playlists, key = { _, playlist -> "p${playlist.id}" }) { index, playlist -> - SpotifyLibraryRow(playlist.artRes, playlist.name, "${playlist.owner} · ${playlist.tracks.size} tracks", Modifier.listFade(listState, slot + index)) { onPlaylist(playlist) } + SpotifyLibraryRow(playlist.artRes, playlist.name, "${playlist.owner} · ${playlist.trackCount} tracks", Modifier.listFade(listState, slot + index), artUrl = playlist.artUrl) { onPlaylist(playlist) } } slot += results.playlists.size } if (results.shows.isNotEmpty()) { item(key = "showsLabel") { SectionLabel("Shows") } itemsIndexed(results.shows, key = { _, show -> "s${show.id}" }) { index, show -> - SpotifyLibraryRow(show.artRes, show.name, show.publisher, Modifier.listFade(listState, slot + index)) { onShow(show) } + SpotifyLibraryRow(show.artRes, show.name, show.publisher, Modifier.listFade(listState, slot + index), artUrl = show.artUrl) { onShow(show) } } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index 0aa11bfd..b5c65ce8 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -79,7 +79,7 @@ fun SpotifySettingsScreen( title = "Settings", actions = actions, options = listOf( - SideAction("about", "About", SidekitIcon.Docs, onClick = { onNotice("Sidetracks 0.1 · a Sidekit UI mockup") }), + SideAction("about", "About", SidekitIcon.Docs, onClick = { onNotice("Sidetracks · a Sidekit sub-app") }), SideAction("cache", "Clear cache", SidekitIcon.DeleteForever, onClick = onClearCache) ) ) { padding -> @@ -146,7 +146,7 @@ fun SpotifySettingsScreen( SidekitItem( "Account", Modifier.listFade(listState, 5), - secondary = if (state.signedIn) "Signed in as You" else "Not signed in", + secondary = if (state.signedIn) "Signed in" else "Not signed in", secondaryToEnd = true ) } @@ -167,7 +167,6 @@ fun SpotifySignInScreen( state: SpotifyUiState, onBack: () -> Unit, onLogIn: () -> Unit, - onCopyLink: () -> Unit, onDismissNotice: () -> Unit ) { val colors = SidekitTheme.colors @@ -178,8 +177,7 @@ fun SpotifySignInScreen( ) SidekitScaffold( title = "Sign in", - actions = actions, - options = listOf(SideAction("copy", "Copy link", SidekitIcon.Docs, onClick = onCopyLink)) + actions = actions ) { padding -> val listState = rememberLazyListState() SidekitList(padding, state = listState) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index cc1f2c54..0d20fcaa 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -9,6 +9,7 @@ import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifySession import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -16,9 +17,11 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.json.JSONObject data class SpotifyUiState( - val signedIn: Boolean = true, + val signedIn: Boolean = false, val current: SpotifyMediaItem? = null, val upcoming: List = emptyList(), val history: List = emptyList(), @@ -27,13 +30,20 @@ data class SpotifyUiState( val searchResults: SpotifySearchResults = SpotifySearchResults(), val searchPending: Boolean = false, val authPending: Boolean = false, + val libraryLoading: Boolean = false, val isPlaying: Boolean = false, val positionMs: Long = 0L, val shuffle: Boolean = false, val repeat: RepeatMode = RepeatMode.Off, - val savedTrackIds: Set = MockSpotifyDataSource.liked.map { it.id }.toSet(), - val savedCollectionKeys: Set = setOf("playlist:late_night", "album:night_signals"), - val followedArtistIds: Set = setOf("aurora", "mira"), + val savedTrackIds: Set = emptySet(), + val savedCollectionKeys: Set = emptySet(), + val followedArtistIds: Set = emptySet(), + val collections: Map = emptyMap(), + val artists: Map = emptyMap(), + val artistAlbums: Map> = emptyMap(), + val artistTracks: Map> = emptyMap(), + val pendingCollections: Set = emptySet(), + val pendingArtists: Set = emptySet(), val filter: LibraryFilter = LibraryFilter.LikedSongs, val searchScopes: Set = SearchScope.entries.toSet(), val query: String = "", @@ -50,12 +60,13 @@ data class SpotifyUiState( * implements the optimistic save/follow behaviour the action bars expect. */ class SpotifyViewModel( - private val catalog: SpotifyCatalog = MockSpotifyDataSource, + private val catalog: SpotifyCatalog, private val history: SpotifyHistoryRepository = SpotifyHistoryRepository(), private val session: SpotifySession? = null, - private val openBrowser: (suspend (String) -> Unit)? = null + private val openBrowser: (suspend (String) -> Unit)? = null, + private val clearArtworkCache: () -> Unit = {} ) : ViewModel() { - private val _state = MutableStateFlow(SpotifyUiState(signedIn = session?.signedIn?.value ?: true)) + private val _state = MutableStateFlow(SpotifyUiState(signedIn = session?.signedIn?.value ?: false)) val state: StateFlow = _state.asStateFlow() private val queue = SpotifyQueueRepository() @@ -64,33 +75,99 @@ class SpotifyViewModel( init { session?.let { viewModelScope.launch { - it.signedIn.collect { signed -> _state.update { state -> state.copy(signedIn = signed) } } + it.signedIn.collect { signed -> + _state.update { state -> state.copy(signedIn = signed) } + if (signed) loadAccount() else clearAccount() + } } } - if (history.items().isEmpty()) { - // Seed the reference account in the same order the fixture lists it: most recent first. - catalog.recentlyPlayed().asReversed().forEach { history.record(it, 0L) } - } - loadAccount() - viewModelScope.launch { - while (true) { - delay(1_000L) - tick() + if (session == null || !session.nativeAvailable) { + // Local fixtures and builds without the native library still need a moving progress bar. + viewModelScope.launch { + while (true) { + delay(1_000L) + tick() + } + } + } else { + viewModelScope.launch { + while (true) { + delay(300L) + val raw = withContext(Dispatchers.IO) { session.pollPlaybackEvent() } ?: continue + handlePlaybackEvent(raw) + } } } } fun dismissNotice() = _state.update { it.copy(notice = null) } + fun refresh() = loadAccount() + fun notice(text: String) = _state.update { it.copy(notice = text) } - fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = catalog.collection(type, id) + fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = _state.value.collections[collectionKey(type, id)] + + fun artist(id: String): SpotifyArtist? = _state.value.artists[id] - fun artist(id: String): SpotifyArtist? = catalog.artist(id) + fun albumsBy(artist: SpotifyArtist): List = _state.value.artistAlbums[artist.id].orEmpty() - fun albumsBy(artist: SpotifyArtist): List = catalog.albumsBy(artist) + fun topTracks(artist: SpotifyArtist): List = _state.value.artistTracks[artist.id].orEmpty() + + fun ensureCollection(type: SpotifyCollectionType, id: String) { + val key = collectionKey(type, id) + val snapshot = _state.value + if (key in snapshot.collections || key in snapshot.pendingCollections) return + _state.update { it.copy(pendingCollections = it.pendingCollections + key) } + viewModelScope.launch { + try { + val collection = catalog.collection(type, id) + _state.update { + it.copy( + collections = if (collection == null) it.collections else it.collections + (key to collection), + pendingCollections = it.pendingCollections - key + ) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy(pendingCollections = it.pendingCollections - key, notice = playbackNotice(e)) + } + } + } + } - fun topTracks(artist: SpotifyArtist): List = catalog.topTracks(artist) + fun ensureArtist(id: String) { + val snapshot = _state.value + if (id in snapshot.artists || id in snapshot.pendingArtists) return + _state.update { it.copy(pendingArtists = it.pendingArtists + id) } + viewModelScope.launch { + try { + val artist = catalog.artist(id) + if (artist == null) { + _state.update { it.copy(pendingArtists = it.pendingArtists - id) } + return@launch + } + val albums = catalog.albumsBy(artist) + val tracks = catalog.topTracks(artist) + _state.update { + it.copy( + artists = it.artists + (id to artist), + artistAlbums = it.artistAlbums + (id to albums), + artistTracks = it.artistTracks + (id to tracks), + pendingArtists = it.pendingArtists - id + ) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy(pendingArtists = it.pendingArtists - id, notice = playbackNotice(e)) + } + } + } + } fun play(items: List, index: Int) { if (index !in items.indices) return @@ -110,7 +187,7 @@ class SpotifyViewModel( fun playCollection(collection: SpotifyCollection, index: Int) = play(collection.items, index) - fun playArtist(artist: SpotifyArtist) = play(catalog.topTracks(artist), 0) + fun playArtist(artist: SpotifyArtist) = play(topTracks(artist), 0) fun playLibraryTrack(track: SpotifyTrack) { val recent = _state.value.recent @@ -200,11 +277,11 @@ class SpotifyViewModel( it.copy(savedTrackIds = saved) } - fun savedCollection(collection: SpotifyCollection): Boolean = collectionKey(collection) in _state.value.savedCollectionKeys + fun savedCollection(collection: SpotifyCollection): Boolean = collectionKey(collection.type, collection.id) in _state.value.savedCollectionKeys fun toggleSaved(collection: SpotifyCollection) = _state.update { val keys = it.savedCollectionKeys.toMutableSet() - val key = collectionKey(collection) + val key = collectionKey(collection.type, collection.id) val wasSaved = keys.contains(key) if (!keys.add(key)) keys.remove(key) it.copy( @@ -226,8 +303,9 @@ class SpotifyViewModel( it.copy(followedArtistIds = ids, notice = if (wasFollowing) "Unfollowed ${artist.name}" else "Following ${artist.name}") } - fun setFilter(filter: LibraryFilter) = _state.update { - it.copy(filter = filter, libraryItems = catalog.library(filter)) + fun setFilter(filter: LibraryFilter) { + _state.update { it.copy(filter = filter) } + loadLibrary(filter) } fun setQuery(query: String) { @@ -250,7 +328,13 @@ class SpotifyViewModel( fun cycleAudioQuality() = _state.update { it.copy(audioQuality = it.audioQuality.next()) } - fun setVolume(value: Float) = _state.update { it.copy(volume = value.coerceIn(0f, 1f)) } + fun setVolume(value: Float) { + val volume = value.coerceIn(0f, 1f) + _state.update { it.copy(volume = volume) } + session?.let { session -> + viewModelScope.launch { runCatching { session.setVolume(volume) } } + } + } fun clearHistory() { history.clear() @@ -259,10 +343,9 @@ class SpotifyViewModel( _state.update { it.copy(recent = emptyList(), notice = "Listening history cleared") } } - fun clearCache() = _state.update { it.copy(notice = "Artwork cache cleared") } - - fun addCurrentToPlaylist() = _state.update { - it.copy(notice = "Added to Late Night Drive") + fun clearCache() { + clearArtworkCache() + _state.update { it.copy(notice = "Artwork cache cleared") } } fun moveToNext(item: SpotifyMediaItem) { @@ -305,24 +388,14 @@ class SpotifyViewModel( fun signOut() { session?.signOut() queue.stop() - _state.update { - SpotifyUiState( - signedIn = false, - savedTrackIds = emptySet(), - savedCollectionKeys = emptySet(), - followedArtistIds = emptySet(), - recent = history.items().filterIsInstance() - ) - } + _state.update { SpotifyUiState(signedIn = false, recent = history.items().filterIsInstance()) } } fun signIn() { val session = session val openBrowser = openBrowser if (session == null || openBrowser == null) { - // Screenshot and unit builds keep the local reference account. - _state.update { SpotifyUiState(signedIn = true) } - loadAccount() + _state.update { it.copy(notice = "Sign-in is unavailable in this build") } return } if (_state.value.authPending) return @@ -343,14 +416,59 @@ class SpotifyViewModel( } private fun loadAccount() { - _state.update { - it.copy( - recent = history.items().filterIsInstance().ifEmpty { catalog.recentlyPlayed() }, - libraryItems = catalog.library(it.filter) - ) + if (!_state.value.signedIn) return + _state.update { it.copy(libraryLoading = true) } + viewModelScope.launch { + try { + val recent = catalog.recentlyPlayed() + val library = catalog.library(_state.value.filter) + _state.update { + it.copy( + recent = recent.ifEmpty { history.items().filterIsInstance() }, + libraryItems = library, + libraryLoading = false + ) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(libraryLoading = false, notice = playbackNotice(e)) } + } + } + } + + private fun loadLibrary(filter: LibraryFilter) { + if (!_state.value.signedIn) return + _state.update { it.copy(libraryLoading = true) } + viewModelScope.launch { + try { + val items = catalog.library(filter) + _state.update { + if (it.filter == filter) it.copy(libraryItems = items, libraryLoading = false) else it + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(libraryLoading = false, notice = playbackNotice(e)) } + } } } + private fun clearAccount() = _state.update { + it.copy( + recent = emptyList(), + libraryItems = emptyList(), + searchResults = SpotifySearchResults(), + collections = emptyMap(), + artists = emptyMap(), + artistAlbums = emptyMap(), + artistTracks = emptyMap(), + savedTrackIds = emptySet(), + savedCollectionKeys = emptySet(), + followedArtistIds = emptySet() + ) + } + private fun applyQueue( snapshot: SpotifyQueueRepository.Snapshot, positionMs: Long? = null, @@ -385,11 +503,7 @@ class SpotifyViewModel( delay(debounceMs) val scopes = _state.value.searchScopes val results = try { - if (session != null && session.signedIn.value) { - SpotifySearchResults(tracks = session.searchTracks(query)) - } else { - catalog.search(query, scopes) - } + if (_state.value.signedIn) catalog.search(query, scopes) else SpotifySearchResults() } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -406,6 +520,52 @@ class SpotifyViewModel( } } + /** + * librespot is the source of truth while it is playing: its position events keep the seek bar + * honest through buffering and seeks, and EndOfTrack advances the Sidekit queue exactly once. + */ + private fun handlePlaybackEvent(raw: String) { + val event = runCatching { JSONObject(raw) }.getOrNull() ?: return + val position = event.optLong("positionMs", _state.value.positionMs) + when (event.optString("type")) { + "loading" -> if (matchesCurrent(event)) { + _state.update { it.copy(positionMs = position) } + } + + "playing" -> if (matchesCurrent(event)) { + _state.update { it.copy(isPlaying = true, positionMs = position) } + } + + "paused" -> if (matchesCurrent(event)) { + _state.update { it.copy(isPlaying = false, positionMs = position) } + } + + "position" -> _state.update { it.copy(positionMs = position) } + + "ended" -> if (matchesCurrent(event)) onPlaybackEnded() + + "unavailable" -> if (matchesCurrent(event)) { + _state.update { it.copy(isPlaying = false, notice = "This track isn't available") } + } + } + } + + private fun matchesCurrent(event: JSONObject): Boolean { + val uri = event.optString("uri").takeIf { it.isNotEmpty() } ?: return true + return _state.value.current?.uri?.equals(uri, ignoreCase = true) ?: true + } + + private fun onPlaybackEnded() { + val snapshot = _state.value + val current = snapshot.current ?: return + if (snapshot.repeat == RepeatMode.Track) { + _state.update { it.copy(positionMs = 0L, isPlaying = true) } + startRemote(current, 0L) + } else { + advance(auto = true, forceContextLoop = snapshot.repeat == RepeatMode.Context) + } + } + private fun tick() { val s = _state.value val current = s.current ?: return @@ -448,7 +608,7 @@ class SpotifyViewModel( } } - /** Starts a real Spotify item; mock fixtures have no URI and keep the local simulation. */ + /** Starts a real Spotify item; local test fixtures have no URI and keep the local simulation. */ private fun startRemote(item: SpotifyMediaItem, positionMs: Long) { val uri = item.uri ?: return val session = session ?: return @@ -468,10 +628,10 @@ class SpotifyViewModel( SpotifyFailure.NotConnected -> "Playback is not ready — try again" SpotifyFailure.AuthenticationExpired -> "Spotify session expired — sign in again" SpotifyFailure.Network -> "Couldn't reach Spotify" - SpotifyFailure.RateLimited -> "Spotify is busy — try again" + SpotifyFailure.RateLimited -> "Spotify is rate-limiting requests — try again shortly" SpotifyFailure.TrackUnavailable -> "This track isn't available" SpotifyFailure.Unknown, null -> error.message ?: "Playback failed" } - private fun collectionKey(collection: SpotifyCollection) = "${collection.type.route}:${collection.id}" + private fun collectionKey(type: SpotifyCollectionType, id: String) = "${type.route}:$id" } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index 250678ef..e2c07ee0 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -13,8 +13,14 @@ object NativeBridge : SpotifyNative { override fun initialize(): Boolean = isAvailable && runCatching { nativeInit() }.getOrDefault(false) + override fun setTmpDir(path: String) { + if (isAvailable) runCatching { nativeSetTmpDir(path) } + } + override fun connect(accessToken: String): Boolean = isAvailable && runCatching { nativeConnect(accessToken) }.getOrDefault(false) + override fun setAccessToken(accessToken: String): Boolean = isAvailable && runCatching { nativeSetAccessToken(accessToken) }.getOrDefault(false) + override fun disconnect() { if (isAvailable) runCatching { nativeDisconnect() } } @@ -41,8 +47,22 @@ object NativeBridge : SpotifyNative { override fun pollEvent(): String? = if (isAvailable) runCatching { nativePollEvent() }.getOrNull() else null + override fun catalogLibrary(filter: String): String? = if (isAvailable) runCatching { nativeCatalogLibrary(filter) }.getOrNull() else null + + override fun catalogCollection(kind: String, id: String): String? = if (isAvailable) runCatching { nativeCatalogCollection(kind, id) }.getOrNull() else null + + override fun catalogArtist(id: String): String? = if (isAvailable) runCatching { nativeCatalogArtist(id) }.getOrNull() else null + + override fun catalogAlbumsBy(id: String): String? = if (isAvailable) runCatching { nativeCatalogAlbumsBy(id) }.getOrNull() else null + + override fun catalogTopTracks(id: String): String? = if (isAvailable) runCatching { nativeCatalogTopTracks(id) }.getOrNull() else null + + override fun catalogSearch(query: String, scopes: String, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogSearch(query, scopes, limit) }.getOrNull() else null + private external fun nativeInit(): Boolean + private external fun nativeSetTmpDir(path: String) private external fun nativeConnect(accessToken: String): Boolean + private external fun nativeSetAccessToken(accessToken: String): Boolean private external fun nativeDisconnect() private external fun nativeCreatePlayer(): Boolean private external fun nativeLoad(uri: String, positionMs: Long, autoplay: Boolean): Boolean @@ -54,4 +74,10 @@ object NativeBridge : SpotifyNative { private external fun nativeReadAudio(timeoutMs: Long): ByteArray? private external fun nativeAudioFormat(): String? private external fun nativePollEvent(): String? + private external fun nativeCatalogLibrary(filter: String): String? + private external fun nativeCatalogCollection(kind: String, id: String): String? + private external fun nativeCatalogArtist(id: String): String? + private external fun nativeCatalogAlbumsBy(id: String): String? + private external fun nativeCatalogTopTracks(id: String): String? + private external fun nativeCatalogSearch(query: String, scopes: String, limit: Long): String? } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt index 903a234c..7e84a902 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -7,7 +7,9 @@ interface SpotifyNative { val isAvailable: Boolean fun initialize(): Boolean + fun setTmpDir(path: String) fun connect(accessToken: String): Boolean + fun setAccessToken(accessToken: String): Boolean fun disconnect() fun createPlayer(): Boolean fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean @@ -19,4 +21,11 @@ interface SpotifyNative { fun readAudio(timeoutMs: Long): ByteArray? fun audioFormat(): String? fun pollEvent(): String? + + fun catalogLibrary(filter: String): String? + fun catalogCollection(kind: String, id: String): String? + fun catalogArtist(id: String): String? + fun catalogAlbumsBy(id: String): String? + fun catalogTopTracks(id: String): String? + fun catalogSearch(query: String, scopes: String, limit: Long): String? } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt new file mode 100644 index 00000000..34792e84 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt @@ -0,0 +1,255 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SearchScope +import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum +import hrln.interactive.sidekit.apps.spotify.SpotifyArtist +import hrln.interactive.sidekit.apps.spotify.SpotifyCollection +import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifyEpisode +import hrln.interactive.sidekit.apps.spotify.SpotifyPlaylist +import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults +import hrln.interactive.sidekit.apps.spotify.SpotifyShow +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack +import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.json.JSONArray +import org.json.JSONObject + +/** + * [SpotifyCatalog] over librespot's authenticated first-party endpoints. + * + * The public Web API shares a quota that ordinary desktop-client credentials exhaust quickly. + * These calls use the same session that plays audio, so they avoid that quota entirely. + */ +class NativeSpotifyCatalog( + private val native: SpotifyNative, + private val ensureSession: suspend () -> Unit +) : SpotifyCatalog { + /** The protocol has no safe recently-played endpoint; the ViewModel falls back to local history. */ + override suspend fun recentlyPlayed(): List = emptyList() + + override suspend fun library(filter: LibraryFilter): List { + val array = parseArray(callText { native.catalogLibrary(filter.name) }) + return when (filter) { + LibraryFilter.LikedSongs -> array.tracks() + LibraryFilter.Playlists -> array.playlists() + LibraryFilter.Albums -> array.albums() + LibraryFilter.Artists -> array.artists() + LibraryFilter.Shows -> array.shows() + } + } + + override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? { + val json = call { native.catalogCollection(type.name, id) } + return runCatching { json.collection(type) }.getOrNull() + } + + override suspend fun artist(id: String): SpotifyArtist? { + val json = call { native.catalogArtist(id) } + return runCatching { artistFromNative(json) }.getOrNull() + } + + override suspend fun albumsBy(artist: SpotifyArtist): List { + val array = parseArray(callText { native.catalogAlbumsBy(artist.id) }) + return array.albums() + } + + override suspend fun topTracks(artist: SpotifyArtist): List { + val array = parseArray(callText { native.catalogTopTracks(artist.id) }) + return array.tracks() + } + + override suspend fun search(query: String, scopes: Set): SpotifySearchResults { + val scopeJson = JSONArray(scopes.map { it.name }).toString() + val json = call { native.catalogSearch(query.trim(), scopeJson, 20L) } + return SpotifySearchResults( + tracks = json.optJSONArray("tracks").tracks(), + artists = json.optJSONArray("artists").artists(), + albums = json.optJSONArray("albums").albums(), + playlists = json.optJSONArray("playlists").playlists(), + shows = json.optJSONArray("shows").shows() + ) + } + + private suspend fun call(block: () -> String?): JSONObject { + val text = callText(block) + return runCatching { JSONObject(text) }.getOrElse { + fail(SpotifyFailure.Unknown, "Spotify returned an unreadable catalog response") + } + } + + private suspend fun callText(block: () -> String?): String = withContext(Dispatchers.IO) { + ensureSession() + val text = block() ?: fail(SpotifyFailure.Network, "Spotify didn't return a catalog response") + if (text.startsWith("{")) { + val error = runCatching { JSONObject(text).optString("error") }.getOrDefault("") + if (error.isNotEmpty()) { + android.util.Log.w("SidetracksCatalog", error) + val failure = if (error.contains("SESSION", ignoreCase = true)) { + SpotifyFailure.AuthenticationExpired + } else { + SpotifyFailure.Network + } + fail(failure, error) + } + } + text + } + + private fun parseArray(text: String): JSONArray = runCatching { JSONArray(text) }.getOrElse { + fail(SpotifyFailure.Unknown, "Spotify returned an unreadable catalog response") + } + + private fun fail(failure: SpotifyFailure, message: String): Nothing = throw SpotifyGatewayException(failure, message) +} + +private fun JSONArray?.tracks(): List = buildList { + if (this@tracks == null) return@buildList + for (i in 0 until this@tracks.length()) { + this@tracks.optJSONObject(i)?.let(::trackFromNative)?.let(::add) + } +} + +private fun JSONArray?.albums(): List = buildList { + if (this@albums == null) return@buildList + for (i in 0 until this@albums.length()) { + this@albums.optJSONObject(i)?.let(::albumFromNative)?.let(::add) + } +} + +private fun JSONArray?.artists(): List = buildList { + if (this@artists == null) return@buildList + for (i in 0 until this@artists.length()) { + this@artists.optJSONObject(i)?.let(::artistFromNative)?.let(::add) + } +} + +private fun JSONArray?.playlists(): List = buildList { + if (this@playlists == null) return@buildList + for (i in 0 until this@playlists.length()) { + this@playlists.optJSONObject(i)?.let(::playlistFromNative)?.let(::add) + } +} + +private fun JSONArray?.shows(): List = buildList { + if (this@shows == null) return@buildList + for (i in 0 until this@shows.length()) { + this@shows.optJSONObject(i)?.let(::showFromNative)?.let(::add) + } +} + +private fun JSONObject.collection(type: SpotifyCollectionType): SpotifyCollection? { + val id = optString("id").takeIf { it.isNotEmpty() } ?: return null + val items = when (type) { + SpotifyCollectionType.Show -> optJSONArray("items").episodes(null) + SpotifyCollectionType.Album, SpotifyCollectionType.Playlist -> optJSONArray("items").tracks() + } + return SpotifyCollection( + type = type, + id = id, + title = optString("title", "Unknown"), + subtitle = optString("subtitle"), + artRes = 0, + items = items, + artistId = optString("artistId").takeIf { it.isNotEmpty() }, + artUrl = optString("artUrl").takeIf { it.isNotEmpty() } + ) +} + +private fun trackFromNative(json: JSONObject): SpotifyTrack? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + return SpotifyTrack( + id = id, + title = json.optString("title", "Unknown track"), + artist = json.optString("artist", "Unknown artist"), + artistId = json.optString("artistId"), + albumId = json.optString("albumId"), + artRes = 0, + durationMs = json.optLong("durationMs", 0L), + explicit = json.optBoolean("explicit", false), + playable = json.optBoolean("playable", true), + uri = json.optString("uri").takeIf { it.isNotEmpty() }, + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() } + ) +} + +private fun albumFromNative(json: JSONObject): SpotifyAlbum? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + return SpotifyAlbum( + id = id, + title = json.optString("title", "Unknown album"), + artist = json.optString("artist", "Unknown artist"), + artistId = json.optString("artistId"), + year = json.optInt("year", 0), + artRes = 0, + tracks = json.optJSONArray("tracks").tracks(), + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() }, + trackCount = json.optInt("trackCount", 0) + ) +} + +private fun artistFromNative(json: JSONObject): SpotifyArtist? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val albumIds = json.optJSONArray("albumIds") ?: JSONArray() + return SpotifyArtist( + id = id, + name = json.optString("name", "Unknown artist"), + artRes = 0, + albumIds = buildList { + for (i in 0 until albumIds.length()) albumIds.optString(i).takeIf { it.isNotEmpty() }?.let(::add) + }, + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() }, + albumCount = json.optInt("albumCount", albumIds.length()) + ) +} + +private fun playlistFromNative(json: JSONObject): SpotifyPlaylist? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + return SpotifyPlaylist( + id = id, + name = json.optString("name", "Unknown playlist"), + owner = json.optString("owner", "Spotify"), + ownerId = json.optString("ownerId").takeIf { it.isNotEmpty() }, + artRes = 0, + tracks = json.optJSONArray("tracks").tracks(), + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() }, + trackCount = json.optInt("trackCount", 0) + ) +} + +private fun showFromNative(json: JSONObject): SpotifyShow? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + return SpotifyShow( + id = id, + name = json.optString("name", "Unknown show"), + publisher = json.optString("publisher", "Podcast"), + artRes = 0, + episodes = json.optJSONArray("episodes").episodes(null), + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() }, + episodeCount = json.optInt("episodeCount", 0) + ) +} + +private fun JSONArray?.episodes(show: SpotifyShow?): List = buildList { + if (this@episodes == null) return@buildList + for (i in 0 until this@episodes.length()) { + val json = this@episodes.optJSONObject(i) ?: continue + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: continue + add( + SpotifyEpisode( + id = id, + title = json.optString("title", "Unknown episode"), + show = json.optString("show").ifEmpty { show?.name ?: "Podcast" }, + showId = json.optString("showId").ifEmpty { show?.id.orEmpty() }, + published = json.optString("published"), + artRes = 0, + durationMs = json.optLong("durationMs", 0L), + video = json.optBoolean("video", false), + uri = json.optString("uri").takeIf { it.isNotEmpty() }, + artUrl = json.optString("artUrl").takeIf { it.isNotEmpty() } + ) + ) + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt index fa00ec7d..9f063f06 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt @@ -12,13 +12,31 @@ class NativeSpotifyGateway( private val native: SpotifyNative, private val accessToken: suspend () -> String? ) : SpotifyGateway { + @Volatile + private var connected = false + + @Volatile + private var sessionConnected = false + + @Volatile + private var sessionToken: String? = null + override suspend fun connect(accessToken: String) = onIo { requireNative() - if (!native.connect(accessToken)) fail(SpotifyFailure.AuthenticationExpired, "Could not connect") - if (!native.createPlayer()) fail(SpotifyFailure.Unknown, "Could not create the player") + if (connected) return@onIo + connectSession(accessToken) + if (!native.createPlayer()) { + native.disconnect() + sessionConnected = false + fail(SpotifyFailure.Unknown, "Could not create the player") + } + connected = true } override suspend fun disconnect() = onIo { + connected = false + sessionConnected = false + sessionToken = null if (native.isAvailable) native.disconnect() } @@ -52,12 +70,44 @@ class NativeSpotifyGateway( native.setVolume(volume) } + /** Forgets the native session after it has been torn down outside this gateway. */ + fun reset() { + connected = false + sessionConnected = false + sessionToken = null + } + /** Connects with the current stored token, refreshing it first when needed. */ suspend fun ensureConnected() { val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") connect(token) } + /** Connects the librespot session without creating an audio player. Used by catalog reads. */ + suspend fun ensureSession() = onIo { + requireNative() + val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + if (!sessionConnected) { + connectSession(token) + } else if (token != sessionToken) { + if (!native.setAccessToken(token)) { + // The native session is gone; reconnect from scratch rather than keep a stale token. + sessionConnected = false + sessionToken = null + connectSession(token) + } else { + sessionToken = token + } + } + } + + private fun connectSession(token: String) { + if (sessionConnected) return + if (!native.connect(token)) fail(SpotifyFailure.AuthenticationExpired, "Could not connect") + sessionConnected = true + sessionToken = token + } + private fun requireNative() { if (!native.isAvailable) fail(SpotifyFailure.Unavailable, "Spotify playback is unavailable in this build") } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt index 7fe935a5..d055d2b3 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt @@ -10,16 +10,15 @@ import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults import hrln.interactive.sidekit.apps.spotify.SpotifyTrack /** - * Read side of the Spotify account, as the UI needs it. Native and Web API implementations will - * sit behind this interface; the mockup uses the local catalog. No screen or ViewModel may call - * the mock data source directly. + * Read side of the Spotify account, as the UI needs it. The production implementation is the Web + * API; tests supply their own catalog. No screen or ViewModel may call an endpoint directly. */ interface SpotifyCatalog { - fun recentlyPlayed(): List - fun library(filter: LibraryFilter): List - fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? - fun artist(id: String): SpotifyArtist? - fun albumsBy(artist: SpotifyArtist): List - fun topTracks(artist: SpotifyArtist): List - fun search(query: String, scopes: Set): SpotifySearchResults + suspend fun recentlyPlayed(): List + suspend fun library(filter: LibraryFilter): List + suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? + suspend fun artist(id: String): SpotifyArtist? + suspend fun albumsBy(artist: SpotifyArtist): List + suspend fun topTracks(artist: SpotifyArtist): List + suspend fun search(query: String, scopes: Set): SpotifySearchResults } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index b63bb471..64c3b499 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -19,7 +19,8 @@ import kotlinx.coroutines.withContext * ViewModel observes [signedIn] and asks for an access token when a real gateway is connected. */ class SpotifySession( - private val auth: SpotifyAuth = SpotifyAuth(NoopCredentialStore) + private val auth: SpotifyAuth = SpotifyAuth(NoopCredentialStore), + private val tmpDir: String? = null ) { private val _signedIn = MutableStateFlow(auth.hasCredentials()) val signedIn: StateFlow = _signedIn.asStateFlow() @@ -32,6 +33,9 @@ class SpotifySession( val gateway: SpotifyGateway get() = nativeGateway private val webApi by lazy { SpotifyWebApi { accessToken() } } + val catalog: SpotifyCatalog by lazy { + if (nativeAvailable) NativeSpotifyCatalog(NativeBridge) { nativeGateway.ensureSession() } else SpotifyWebCatalog(webApi) + } private val playbackScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) val playback: SpotifyPlayback by lazy { @@ -43,7 +47,10 @@ class SpotifySession( } init { - if (nativeAvailable) NativeBridge.initialize() + if (nativeAvailable) { + tmpDir?.let(NativeBridge::setTmpDir) + NativeBridge.initialize() + } } suspend fun signIn(openBrowser: suspend (String) -> Unit) { @@ -52,6 +59,7 @@ class SpotifySession( } fun signOut() { + nativeGateway.reset() NativeBridge.disconnect() auth.clear() _signedIn.value = false @@ -76,7 +84,10 @@ class SpotifySession( suspend fun accessToken(): String? = auth.accessToken() - suspend fun searchTracks(query: String): List = webApi.searchTracks(query) + suspend fun setVolume(volume: Float) = playback.setVolume(volume) + + /** One librespot player event, or null when nothing has happened since the last poll. */ + fun pollPlaybackEvent(): String? = if (nativeAvailable) NativeBridge.pollEvent() else null private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { override fun read(): hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials? = null @@ -92,7 +103,8 @@ object SpotifySessions { fun get(context: Context): SpotifySession = instance ?: synchronized(this) { instance ?: SpotifySession( - SpotifyAuth(EncryptedSpotifyCredentialStore(context.applicationContext)) + SpotifyAuth(EncryptedSpotifyCredentialStore(context.applicationContext)), + context.applicationContext.cacheDir.absolutePath ).also { instance = it } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt index 440b1ab9..5ab5a219 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt @@ -1,6 +1,15 @@ package hrln.interactive.sidekit.apps.spotify.data -import hrln.interactive.sidekit.R +import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SearchScope +import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum +import hrln.interactive.sidekit.apps.spotify.SpotifyArtist +import hrln.interactive.sidekit.apps.spotify.SpotifyCollection +import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifyEpisode +import hrln.interactive.sidekit.apps.spotify.SpotifyPlaylist +import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults +import hrln.interactive.sidekit.apps.spotify.SpotifyShow import hrln.interactive.sidekit.apps.spotify.SpotifyTrack import java.io.IOException import java.net.HttpURLConnection @@ -8,6 +17,7 @@ import java.net.URL import java.net.URLEncoder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.json.JSONArray import org.json.JSONObject /** @@ -15,23 +25,158 @@ import org.json.JSONObject * screens never learn an endpoint or a field name. */ class SpotifyWebApi(private val accessToken: suspend () -> String?) { - suspend fun searchTracks(query: String, limit: Int = 20): List { - val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") - val encoded = URLEncoder.encode(query, Charsets.UTF_8.name()) - val json = get("$SEARCH?q=$encoded&type=track&limit=${limit.coerceIn(1, 50)}&market=from_token", token) - return parseSearchTracks(json) + suspend fun recentlyPlayed(limit: Int = 10): List { + val json = get("$ME_RECENT?limit=${limit.coerceIn(1, 50)}", token()) + val items = json.optJSONArray("items") ?: return emptyList() + return buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) + } + } + } + + suspend fun library(filter: LibraryFilter): List = when (filter) { + LibraryFilter.LikedSongs -> tracks("$ME_TRACKS?limit=50") + LibraryFilter.Playlists -> playlists("$ME_PLAYLISTS?limit=50") + LibraryFilter.Albums -> savedAlbums() + LibraryFilter.Artists -> followedArtists() + LibraryFilter.Shows -> savedShows() + } + + suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { + SpotifyCollectionType.Album -> runCatching { get("$ALBUMS/${enc(id)}", token()) }.getOrNull()?.let(::albumCollection) + SpotifyCollectionType.Playlist -> runCatching { get("$PLAYLISTS/${enc(id)}", token()) }.getOrNull()?.let(::playlistCollection) + SpotifyCollectionType.Show -> runCatching { get("$SHOWS/${enc(id)}", token()) }.getOrNull()?.let(::showCollection) + } + + suspend fun search(query: String, scopes: Set, limit: Int = 20): SpotifySearchResults { + if (query.trim().length < 2) return SpotifySearchResults() + val types = buildList { + if (SearchScope.Tracks in scopes) add("track") + if (SearchScope.Artists in scopes) add("artist") + if (SearchScope.Albums in scopes) add("album") + if (SearchScope.Playlists in scopes) add("playlist") + if (SearchScope.Shows in scopes) add("show") + } + if (types.isEmpty()) return SpotifySearchResults() + val json = get( + "$SEARCH?q=${enc(query.trim())}&type=${types.joinToString(",")}&limit=${limit.coerceIn(1, 50)}&market=from_token", + token() + ) + return SpotifySearchResults( + tracks = json.optJSONObject("tracks")?.optJSONArray("items").tracks(), + artists = json.optJSONObject("artists")?.optJSONArray("items").artists(), + albums = json.optJSONObject("albums")?.optJSONArray("items").albums(), + playlists = json.optJSONObject("playlists")?.optJSONArray("items").playlists(), + shows = json.optJSONObject("shows")?.optJSONArray("items").shows() + ) } + suspend fun artist(id: String): SpotifyArtist? = runCatching { + artist(get("$ARTISTS/${enc(id)}", token())) + }.getOrNull() + + suspend fun albumsBy(artist: SpotifyArtist): List = runCatching { + val json = get("$ARTISTS/${enc(artist.id)}/albums?include_groups=album,single&limit=50&market=from_token", token()) + json.optJSONArray("items").albums() + }.getOrDefault(emptyList()) + + suspend fun topTracks(artist: SpotifyArtist): List = runCatching { + val json = get("$ARTISTS/${enc(artist.id)}/top-tracks?market=from_token", token()) + json.optJSONArray("tracks").tracks() + }.getOrDefault(emptyList()) + /** Visible for tests: maps Spotify's search envelope without touching the network. */ - internal fun parseSearchTracks(json: JSONObject): List { - val items = json.optJSONObject("tracks")?.optJSONArray("items") ?: return emptyList() + internal fun parseSearchTracks(json: JSONObject): List = json.optJSONObject("tracks")?.optJSONArray("items").tracks() + + private suspend fun tracks(url: String): List { + val items = get(url, token()).optJSONArray("items") ?: return emptyList() return buildList(items.length()) { for (i in 0 until items.length()) { - items.optJSONObject(i)?.let(::track)?.let(::add) + items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) } } } + private suspend fun playlists(url: String): List = get(url, token()).optJSONArray("items").playlists() + + private suspend fun savedAlbums(): List { + val items = get("$ME_ALBUMS?limit=50", token()).optJSONArray("items") ?: return emptyList() + return buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.optJSONObject("album")?.let(::album)?.let(::add) + } + } + } + + private suspend fun followedArtists(): List { + val json = get("$ME_FOLLOWING?type=artist&limit=50", token()) + return json.optJSONObject("artists")?.optJSONArray("items").artists() + } + + private suspend fun savedShows(): List { + val items = get("$ME_SHOWS?limit=50", token()).optJSONArray("items") ?: return emptyList() + return buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.optJSONObject("show")?.let(::show)?.let(::add) + } + } + } + + private fun albumCollection(json: JSONObject): SpotifyCollection? { + val album = album(json) ?: return null + val tracks = json.optJSONObject("tracks")?.optJSONArray("items").tracks() + return SpotifyCollection( + type = SpotifyCollectionType.Album, + id = album.id, + title = album.title, + subtitle = "${album.artist} · ${album.year} · ${album.trackCount} tracks", + artRes = 0, + items = tracks, + artistId = album.artistId, + artUrl = album.artUrl + ) + } + + private fun playlistCollection(json: JSONObject): SpotifyCollection? { + val playlist = playlist(json) ?: return null + val items = json.optJSONObject("tracks")?.optJSONArray("items") ?: JSONArray() + val tracks = buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) + } + } + return SpotifyCollection( + type = SpotifyCollectionType.Playlist, + id = playlist.id, + title = playlist.name, + subtitle = "${playlist.owner} · ${playlist.trackCount} tracks", + artRes = 0, + items = tracks, + artistId = playlist.ownerId, + artUrl = playlist.artUrl + ) + } + + private fun showCollection(json: JSONObject): SpotifyCollection? { + val show = show(json) ?: return null + val items = json.optJSONObject("episodes")?.optJSONArray("items") ?: JSONArray() + val episodes = buildList(items.length()) { + for (i in 0 until items.length()) { + items.optJSONObject(i)?.let { episode(it, show) }?.let(::add) + } + } + return SpotifyCollection( + type = SpotifyCollectionType.Show, + id = show.id, + title = show.name, + subtitle = "${show.publisher} · ${show.episodeCount} episodes", + artRes = 0, + items = episodes, + artUrl = show.artUrl + ) + } + private fun track(json: JSONObject): SpotifyTrack? { val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null val album = json.optJSONObject("album") @@ -42,14 +187,153 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { artist = artist?.optString("name").orEmpty().ifEmpty { "Unknown artist" }, artistId = artist?.optString("id").orEmpty(), albumId = album?.optString("id").orEmpty(), - artRes = R.drawable.cover_night_signals, + artRes = 0, durationMs = json.optLong("duration_ms", 0L), explicit = json.optBoolean("explicit", false), - playable = true, - uri = json.optString("uri").takeIf { it.isNotEmpty() } + playable = json.optBoolean("is_playable", true), + uri = json.optString("uri").takeIf { it.isNotEmpty() }, + artUrl = imageUrl(album) ) } + private fun episode(json: JSONObject, show: SpotifyShow? = null): SpotifyEpisode? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val images = imageUrl(json) ?: show?.artUrl + return SpotifyEpisode( + id = id, + title = json.optString("name", "Unknown episode"), + show = show?.name ?: json.optString("show").ifEmpty { "Podcast" }, + showId = show?.id ?: json.optJSONObject("show")?.optString("id").orEmpty(), + published = json.optString("release_date").ifEmpty { "Podcast episode" }, + artRes = 0, + durationMs = json.optLong("duration_ms", 0L), + video = false, + uri = json.optString("uri").takeIf { it.isNotEmpty() }, + artUrl = images + ) + } + + private fun album(json: JSONObject): SpotifyAlbum? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val artist = json.optJSONArray("artists")?.optJSONObject(0) + val tracks = json.optJSONObject("tracks")?.optJSONArray("items").tracks() + return SpotifyAlbum( + id = id, + title = json.optString("name", "Unknown album"), + artist = artist?.optString("name").orEmpty().ifEmpty { "Unknown artist" }, + artistId = artist?.optString("id").orEmpty(), + year = json.optString("release_date").take(4).toIntOrNull() ?: 0, + artRes = 0, + tracks = tracks, + artUrl = imageUrl(json), + trackCount = json.optJSONObject("tracks")?.optInt("total", tracks.size) ?: tracks.size + ) + } + + private fun playlist(json: JSONObject): SpotifyPlaylist? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val owner = json.optJSONObject("owner") + return SpotifyPlaylist( + id = id, + name = json.optString("name", "Unknown playlist"), + owner = owner?.optString("display_name").orEmpty().ifEmpty { "Spotify" }, + ownerId = owner?.optString("id"), + artRes = 0, + tracks = emptyList(), + artUrl = imageUrl(json), + trackCount = json.optJSONObject("tracks")?.optInt("total", 0) ?: 0 + ) + } + + private fun show(json: JSONObject): SpotifyShow? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + val artUrl = imageUrl(json) + val base = SpotifyShow( + id = id, + name = json.optString("name", "Unknown show"), + publisher = json.optString("publisher").ifEmpty { "Podcast" }, + artRes = 0, + episodes = emptyList(), + artUrl = artUrl + ) + val episodes = json.optJSONObject("episodes")?.optJSONArray("items").episodes(this, base) + return base.copy( + episodes = episodes, + episodeCount = json.optJSONObject("episodes")?.optInt("total", episodes.size) ?: episodes.size + ) + } + + private fun artist(json: JSONObject): SpotifyArtist? { + val id = json.optString("id").takeIf { it.isNotEmpty() } ?: return null + return SpotifyArtist( + id = id, + name = json.optString("name", "Unknown artist"), + artRes = 0, + albumIds = emptyList(), + artUrl = imageUrl(json) + ) + } + + private fun JSONArray?.tracks(): List = buildList { + if (this@tracks == null) return@buildList + for (i in 0 until this@tracks.length()) { + this@tracks.optJSONObject(i)?.let(::track)?.let(::add) + } + } + + private fun JSONArray?.albums(): List = buildList { + if (this@albums == null) return@buildList + for (i in 0 until this@albums.length()) { + this@albums.optJSONObject(i)?.let(::album)?.let(::add) + } + } + + private fun JSONArray?.artists(): List = buildList { + if (this@artists == null) return@buildList + for (i in 0 until this@artists.length()) { + this@artists.optJSONObject(i)?.let(::artist)?.let(::add) + } + } + + private fun JSONArray?.playlists(): List = buildList { + if (this@playlists == null) return@buildList + for (i in 0 until this@playlists.length()) { + this@playlists.optJSONObject(i)?.let(::playlist)?.let(::add) + } + } + + private fun JSONArray?.shows(): List = buildList { + if (this@shows == null) return@buildList + for (i in 0 until this@shows.length()) { + this@shows.optJSONObject(i)?.let(::show)?.let(::add) + } + } + + private fun JSONArray?.episodes(api: SpotifyWebApi, show: SpotifyShow): List = buildList { + if (this@episodes == null) return@buildList + for (i in 0 until this@episodes.length()) { + this@episodes.optJSONObject(i)?.let { api.episode(it, show) }?.let(::add) + } + } + + private fun imageUrl(json: JSONObject?): String? { + val images = json?.optJSONArray("images") ?: return null + var best: String? = null + var bestWidth = Int.MAX_VALUE + for (i in 0 until images.length()) { + val image = images.optJSONObject(i) ?: continue + val url = image.optString("url").takeIf { it.isNotEmpty() } ?: continue + val width = image.optInt("width", Int.MAX_VALUE) + if (width >= 64 && width < bestWidth) { + best = url + bestWidth = width + } + } + return best ?: images.optJSONObject(0)?.optString("url")?.takeIf { it.isNotEmpty() } + } + + private suspend fun token(): String = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + private suspend fun get(url: String, token: String): JSONObject = withContext(Dispatchers.IO) { val connection = (URL(url).openConnection() as HttpURLConnection).apply { requestMethod = "GET" @@ -65,9 +349,15 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { when { status == 401 -> fail(SpotifyFailure.AuthenticationExpired, "Sign in again") - status == 429 -> fail(SpotifyFailure.RateLimited, "Spotify is busy — try again") + status == 429 -> { + android.util.Log.w("SidetracksApi", "$url -> 429 retryAfter=${connection.getHeaderField("Retry-After")} ${text.take(300)}") + fail(SpotifyFailure.RateLimited, "Spotify is rate-limiting requests") + } - status !in 200..299 -> fail(SpotifyFailure.Network, "Spotify returned HTTP $status") + status !in 200..299 -> { + android.util.Log.w("SidetracksApi", "$url -> $status ${text.take(300)}") + fail(SpotifyFailure.Network, "Spotify returned HTTP $status") + } else -> runCatching { JSONObject(text) } .getOrElse { fail(SpotifyFailure.Unknown, "Spotify returned an unreadable response") } @@ -81,10 +371,23 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { } } + private fun enc(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name()) + private fun fail(failure: SpotifyFailure, message: String): Nothing = throw SpotifyGatewayException(failure, message) private companion object { - const val SEARCH = "https://api.spotify.com/v1/search" + const val API = "https://api.spotify.com/v1" + const val SEARCH = "$API/search" + const val ME_RECENT = "$API/me/player/recently-played" + const val ME_TRACKS = "$API/me/tracks" + const val ME_PLAYLISTS = "$API/me/playlists" + const val ME_ALBUMS = "$API/me/albums" + const val ME_FOLLOWING = "$API/me/following" + const val ME_SHOWS = "$API/me/shows" + const val ALBUMS = "$API/albums" + const val ARTISTS = "$API/artists" + const val PLAYLISTS = "$API/playlists" + const val SHOWS = "$API/shows" const val TIMEOUT_MS = 15_000 } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt new file mode 100644 index 00000000..79105f62 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt @@ -0,0 +1,27 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SearchScope +import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum +import hrln.interactive.sidekit.apps.spotify.SpotifyArtist +import hrln.interactive.sidekit.apps.spotify.SpotifyCollection +import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack + +/** [SpotifyCatalog] over the Web API. */ +class SpotifyWebCatalog(private val api: SpotifyWebApi) : SpotifyCatalog { + override suspend fun recentlyPlayed(): List = api.recentlyPlayed() + + override suspend fun library(filter: LibraryFilter): List = api.library(filter) + + override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = api.collection(type, id) + + override suspend fun artist(id: String): SpotifyArtist? = api.artist(id) + + override suspend fun albumsBy(artist: SpotifyArtist): List = api.albumsBy(artist) + + override suspend fun topTracks(artist: SpotifyArtist): List = api.topTracks(artist) + + override suspend fun search(query: String, scopes: Set): SpotifySearchResults = api.search(query, scopes) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt similarity index 93% rename from app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt rename to app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt index f81bb6dd..e4fb1be5 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt @@ -4,8 +4,8 @@ import hrln.interactive.sidekit.R import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog /** - * The mockup's account. Everything here is local, fixed data shaped like the gateway response: - * the UI can be reviewed on a device without a Spotify login, a native library, or a network. + * The screenshot suite's account. Everything here is local, fixed data shaped like the gateway + * response: the UI can be reviewed without a Spotify login, a native library, or a network. */ object MockSpotifyDataSource : SpotifyCatalog { private fun t( @@ -179,15 +179,15 @@ object MockSpotifyDataSource : SpotifyCatalog { nightSignals[5], glassHours[1], coastline[6], roomsWeLeft[5] ) - override fun recentlyPlayed(): List = recentlyPlayed + override suspend fun recentlyPlayed(): List = recentlyPlayed fun album(id: String): SpotifyAlbum? = albums.firstOrNull { it.id == id } fun playlist(id: String): SpotifyPlaylist? = playlists.firstOrNull { it.id == id } fun show(id: String): SpotifyShow? = shows.firstOrNull { it.id == id } - override fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } + override suspend fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } fun track(id: String): SpotifyTrack? = albums.flatMap { it.tracks }.firstOrNull { it.id == id } - override fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { + override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { SpotifyCollectionType.Album -> album(id)?.let { SpotifyCollection(type, id, it.title, "${it.artist} · ${it.year} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.artistId) } @@ -201,13 +201,13 @@ object MockSpotifyDataSource : SpotifyCatalog { } } - override fun topTracks(artist: SpotifyArtist): List = topTracks(artist, 10) + override suspend fun topTracks(artist: SpotifyArtist): List = topTracks(artist, 10) fun topTracks(artist: SpotifyArtist, limit: Int): List = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks }.take(limit) - override fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) + override suspend fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) - override fun library(filter: LibraryFilter): List = when (filter) { + override suspend fun library(filter: LibraryFilter): List = when (filter) { LibraryFilter.LikedSongs -> liked LibraryFilter.Playlists -> playlists LibraryFilter.Albums -> albums @@ -215,7 +215,7 @@ object MockSpotifyDataSource : SpotifyCatalog { LibraryFilter.Shows -> shows } - override fun search(query: String, scopes: Set): SpotifySearchResults { + override suspend fun search(query: String, scopes: Set): SpotifySearchResults { val q = query.trim() if (q.length < 2) return SpotifySearchResults() fun match(vararg text: String) = text.any { it.contains(q, ignoreCase = true) } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt index 04e1340a..525df70a 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -53,10 +53,15 @@ class NativeSpotifyGatewayTest { var loadOk = true override fun initialize() = isAvailable + override fun setTmpDir(path: String) = Unit override fun connect(accessToken: String): Boolean { token = accessToken return isAvailable } + override fun setAccessToken(accessToken: String): Boolean { + token = accessToken + return isAvailable + } override fun disconnect() = Unit override fun createPlayer(): Boolean { playerCreated = true @@ -71,5 +76,11 @@ class NativeSpotifyGatewayTest { override fun readAudio(timeoutMs: Long): ByteArray? = null override fun audioFormat(): String? = null override fun pollEvent(): String? = null + override fun catalogLibrary(filter: String): String? = null + override fun catalogCollection(kind: String, id: String): String? = null + override fun catalogArtist(id: String): String? = null + override fun catalogAlbumsBy(id: String): String? = null + override fun catalogTopTracks(id: String): String? = null + override fun catalogSearch(query: String, scopes: String, limit: Long): String? = null } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt index 128622a1..ccbaf71b 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.test.onRoot import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.takahirom.roborazzi.captureRoboImage import hrln.interactive.sidekit.ui.SidekitPreviewFrame +import kotlinx.coroutines.runBlocking import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -28,8 +29,8 @@ class SidetracksScreenshotTest { upcoming = recent.drop(1), history = recent.drop(3), recent = recent, - libraryItems = MockSpotifyDataSource.library(LibraryFilter.Albums), - searchResults = MockSpotifyDataSource.search("night", SearchScope.entries.toSet()), + libraryItems = runBlocking { MockSpotifyDataSource.library(LibraryFilter.Albums) }, + searchResults = runBlocking { MockSpotifyDataSource.search("night", SearchScope.entries.toSet()) }, isPlaying = true, positionMs = 68_000L, savedTrackIds = setOf(current.id), @@ -43,6 +44,7 @@ class SidetracksScreenshotTest { onLibrary = {}, onSearch = {}, onSettings = {}, + onRefresh = {}, onPlay = {}, onDismissNotice = {} ) @@ -59,14 +61,13 @@ class SidetracksScreenshotTest { onAlbum = {}, onArtist = {}, onShow = {}, - onLoadMore = {}, onDismissNotice = {} ) } @Test fun album() = shoot("sidetracks_album") { CollectionScreen( - MockSpotifyDataSource.collection(SpotifyCollectionType.Album, "night_signals")!!, + runBlocking { MockSpotifyDataSource.collection(SpotifyCollectionType.Album, "night_signals") }!!, state, onBack = {}, onPlay = {}, @@ -75,7 +76,6 @@ class SidetracksScreenshotTest { onArtist = {}, onQueue = {}, onNowPlaying = {}, - onLoadMore = {}, onDismissNotice = {} ) } @@ -84,8 +84,8 @@ class SidetracksScreenshotTest { val artist = MockSpotifyDataSource.artists.first() ArtistScreen( artist, - MockSpotifyDataSource.albumsBy(artist), - MockSpotifyDataSource.topTracks(artist), + runBlocking { MockSpotifyDataSource.albumsBy(artist) }, + runBlocking { MockSpotifyDataSource.topTracks(artist) }, state, onBack = {}, onPlay = {}, @@ -127,7 +127,6 @@ class SidetracksScreenshotTest { onQueue = {}, onAlbum = {}, onArtist = {}, - onAddToPlaylist = {}, onDismissNotice = {} ) } @@ -166,7 +165,6 @@ class SidetracksScreenshotTest { SpotifyUiState(signedIn = false), onBack = {}, onLogIn = {}, - onCopyLink = {}, onDismissNotice = {} ) } diff --git a/native/sidekit-spotify/Cargo.lock b/native/sidekit-spotify/Cargo.lock index 630207c8..54cedd38 100644 --- a/native/sidekit-spotify/Cargo.lock +++ b/native/sidekit-spotify/Cargo.lock @@ -529,6 +529,17 @@ version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" +[[package]] +name = "futures-macro" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + [[package]] name = "futures-sink" version = "0.3.34" @@ -555,6 +566,7 @@ checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", @@ -1140,8 +1152,6 @@ dependencies = [ [[package]] name = "librespot-core" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168bbe1c416980ddd9a969ebd6b50fb6c924eb1a3ded194285fa8ec0e2b1c68b" dependencies = [ "aes", "base64 0.22.1", @@ -2261,10 +2271,17 @@ name = "sidekit-spotify" version = "0.1.0" dependencies = [ "android_logger", + "bytes", + "futures-util", + "http", "jni", "librespot-core", + "librespot-metadata", "librespot-playback", + "librespot-protocol", "log", + "protobuf", + "protobuf-json-mapping", "serde", "serde_json", "tokio", diff --git a/native/sidekit-spotify/Cargo.toml b/native/sidekit-spotify/Cargo.toml index 4439a7e0..6733d1b5 100644 --- a/native/sidekit-spotify/Cargo.toml +++ b/native/sidekit-spotify/Cargo.toml @@ -11,7 +11,14 @@ crate-type = ["cdylib"] [dependencies] jni = "0.21" librespot-core = { version = "0.8.0", default-features = false, features = ["rustls-tls-webpki-roots"] } +librespot-metadata = { version = "0.8.0", default-features = false, features = ["rustls-tls-webpki-roots"] } librespot-playback = { version = "0.8.0", default-features = false, features = ["rustls-tls-webpki-roots"] } +librespot-protocol = "0.8.0" +protobuf = "3.7" +protobuf-json-mapping = "3.7" +bytes = "1" +futures-util = "0.3" +http = "1" log = "0.4" serde = { version = "1", features = ["derive"] } serde_json = "1" @@ -26,3 +33,6 @@ lto = true strip = true codegen-units = 1 panic = "abort" + +[patch.crates-io] +librespot-core = { path = "../vendor/librespot-core" } diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs new file mode 100644 index 00000000..7a405ee8 --- /dev/null +++ b/native/sidekit-spotify/src/catalog.rs @@ -0,0 +1,1320 @@ +//! Catalog reads over librespot's authenticated session (spclient + pathfinder). +//! +//! The public Web API has a shared quota that is exhausted by ordinary desktop-client +//! credentials. The first-party endpoints used by the Spotify clients do not share that quota, so +//! Sidetracks reads its library, search, artist and collection data through them instead. + +use std::collections::HashSet; + +use bytes::Bytes; +use futures_util::future::join_all; +use http::header::{HeaderValue, ACCEPT, AUTHORIZATION, CONTENT_TYPE}; +use http::Method; +use librespot_core::spclient::SpClientError; +use librespot_core::Session; +use librespot_core::SpotifyUri; +use librespot_metadata::image::Images; +use librespot_metadata::{Album, Artist, Episode, Metadata, Playlist, Show, Track}; +use librespot_protocol::context::Context; +use librespot_protocol::extended_metadata::{ + BatchedEntityRequest, BatchedExtensionResponse, EntityRequest, ExtensionQuery, +}; +use librespot_protocol::extension_kind::ExtensionKind; +use librespot_protocol::playlist4_external::SelectedListContent; +use protobuf::{EnumOrUnknown, Message}; +use serde::Serialize; +use serde_json::{json, Value}; + +const PATHFINDER_BASE: &str = "https://api-partner.spotify.com"; +const SEARCH_QUERY_HASH: &str = "d9f785900f0710b31c07818d617f4f7600c1e21217e80f5b043d1e78d74e6026"; +const COLLECTION_LIMIT: usize = 500; +const COLLECTION_PAGE: i32 = 100; +const COLLECTION_PAGES: usize = 8; +const PLAYLIST_TRACK_LIMIT: usize = 100; +const SHOW_EPISODE_LIMIT: usize = 50; +const ARTIST_ALBUM_LIMIT: usize = 50; +const ARTIST_TOP_LIMIT: usize = 20; + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +struct TrackJson { + id: String, + title: String, + artist: String, + artist_id: String, + album_id: String, + art_res: i32, + duration_ms: i64, + explicit: bool, + playable: bool, + uri: String, + art_url: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct AlbumJson { + id: String, + title: String, + artist: String, + artist_id: String, + year: i32, + art_res: i32, + tracks: Vec, + art_url: Option, + track_count: i32, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct PlaylistJson { + id: String, + name: String, + owner: String, + owner_id: Option, + art_res: i32, + tracks: Vec, + art_url: Option, + track_count: i32, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct ArtistJson { + id: String, + name: String, + art_res: i32, + album_ids: Vec, + art_url: Option, + album_count: i32, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct EpisodeJson { + id: String, + title: String, + show: String, + show_id: String, + published: String, + art_res: i32, + duration_ms: i64, + video: bool, + uri: String, + art_url: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct ShowJson { + id: String, + name: String, + publisher: String, + art_res: i32, + episodes: Vec, + art_url: Option, + episode_count: i32, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct CollectionJson { + #[serde(rename = "type")] + kind: String, + id: String, + title: String, + subtitle: String, + art_res: i32, + items: Vec, + artist_id: Option, + art_url: Option, +} + +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +struct SearchJson { + tracks: Vec, + artists: Vec, + albums: Vec, + playlists: Vec, + shows: Vec, +} + +fn id_of(uri: &SpotifyUri) -> String { + uri.to_id().unwrap_or_default() +} + +fn uri_of(id: &str, kind: &str) -> String { + if id.starts_with("spotify:") { + id.to_string() + } else { + format!("spotify:{kind}:{id}") + } +} + +fn spotify_uri(id: &str, kind: &str) -> Result { + SpotifyUri::from_uri(&uri_of(id, kind)).map_err(|e| format!("invalid URI: {e}")) +} + +fn image_url(images: &Images) -> Option { + let preferred = [ + librespot_metadata::image::ImageSize::DEFAULT, + librespot_metadata::image::ImageSize::SMALL, + librespot_metadata::image::ImageSize::LARGE, + ]; + for size in preferred { + if let Some(image) = images.iter().find(|image| image.size == size) { + return image + .id + .to_base16() + .ok() + .map(|id| format!("https://i.scdn.co/image/{id}")); + } + } + images + .first() + .and_then(|image| image.id.to_base16().ok()) + .map(|id| format!("https://i.scdn.co/image/{id}")) +} + +fn artist_name_and_id(artist: Option<&librespot_metadata::Artist>) -> (String, String) { + match artist { + Some(artist) => (artist.name.clone(), id_of(&artist.id)), + None => ("Unknown artist".to_string(), String::new()), + } +} + +fn track_json(track: &Track) -> TrackJson { + let (artist, artist_id) = artist_name_and_id(track.artists.first()); + TrackJson { + id: id_of(&track.id), + title: track.name.clone(), + artist, + artist_id, + album_id: id_of(&track.album.id), + art_res: 0, + duration_ms: track.duration as i64, + explicit: track.is_explicit, + playable: track.restrictions.is_empty(), + uri: uri_of(&id_of(&track.id), "track"), + art_url: image_url(&track.album.covers), + } +} + +fn album_json(album: &Album, tracks: Vec) -> AlbumJson { + let (artist, artist_id) = artist_name_and_id(album.artists.first()); + AlbumJson { + id: id_of(&album.id), + title: album.name.clone(), + artist, + artist_id, + year: album.date.year() as i32, + art_res: 0, + track_count: album.tracks().count() as i32, + tracks, + art_url: image_url(&album.covers), + } +} + +fn playlist_owner(playlist: &Playlist) -> (String, Option) { + match &playlist.id { + SpotifyUri::Playlist { user, .. } => { + let owner = user.clone().unwrap_or_default(); + let id = if owner.is_empty() { + None + } else { + Some(owner.clone()) + }; + ( + if owner.is_empty() { + "Spotify".to_string() + } else { + owner + }, + id, + ) + } + _ => ("Spotify".to_string(), None), + } +} + +fn playlist_art(playlist: &Playlist) -> Option { + playlist + .attributes + .picture_sizes + .iter() + .find(|size| !size.url.is_empty()) + .map(|size| size.url.clone()) +} + +fn playlist_json(playlist: &Playlist, tracks: Vec) -> PlaylistJson { + let (owner, owner_id) = playlist_owner(playlist); + PlaylistJson { + id: id_of(&playlist.id), + name: playlist.name().to_string(), + owner, + owner_id, + art_res: 0, + tracks, + art_url: playlist_art(playlist), + track_count: playlist.length.max(0), + } +} + +fn artist_json(artist: &Artist, album_ids: Vec) -> ArtistJson { + ArtistJson { + id: id_of(&artist.id), + name: artist.name.clone(), + art_res: 0, + album_count: album_ids.len() as i32, + album_ids, + art_url: image_url(&artist.portraits).or_else(|| image_url(&artist.portrait_group)), + } +} + +fn episode_json(episode: &Episode, show: &Show) -> EpisodeJson { + EpisodeJson { + id: id_of(&episode.id), + title: episode.name.clone(), + show: if episode.show_name.is_empty() { + show.name.clone() + } else { + episode.show_name.clone() + }, + show_id: id_of(&show.id), + published: { + let date = episode.publish_time.as_utc(); + format!( + "{:04}-{:02}-{:02}", + date.year(), + u8::from(date.month()), + date.day() + ) + }, + art_res: 0, + duration_ms: episode.duration as i64, + video: false, + uri: uri_of(&id_of(&episode.id), "episode"), + art_url: image_url(&episode.covers).or_else(|| image_url(&show.covers)), + } +} + +fn show_json(show: &Show, episodes: Vec) -> ShowJson { + ShowJson { + id: id_of(&show.id), + name: show.name.clone(), + publisher: if show.publisher.is_empty() { + "Podcast".to_string() + } else { + show.publisher.clone() + }, + art_res: 0, + episode_count: show.episodes.len() as i32, + episodes, + art_url: image_url(&show.covers), + } +} + +async fn http_request( + session: &Session, + token: &str, + method: Method, + url: &str, + content_type: Option<&str>, + body: &[u8], +) -> Result { + let mut builder = http::Request::builder() + .method(method) + .uri(url) + .header(AUTHORIZATION, format!("Bearer {token}")); + if let Some(content_type) = content_type { + builder = builder.header(CONTENT_TYPE, content_type); + } + let request = builder + .body(Bytes::copy_from_slice(body)) + .map_err(|e| e.to_string())?; + session + .http_client() + .request_body(request) + .await + .map_err(|e| e.to_string()) +} + +async fn sp_request( + session: &Session, + token: &str, + method: Method, + endpoint: &str, + content_type: Option<&str>, + body: &[u8], +) -> Result { + let (host, port) = session + .spclient() + .get_accesspoint() + .await + .map_err(|e| e.to_string())?; + let url = if port == 443 { + format!("https://{host}{endpoint}") + } else { + format!("https://{host}:{port}{endpoint}") + }; + http_request(session, token, method, &url, content_type, body).await +} + +async fn metadata_bytes( + session: &Session, + token: &str, + kind: ExtensionKind, + uri: &SpotifyUri, +) -> Result { + let request = BatchedEntityRequest { + entity_request: vec![EntityRequest { + entity_uri: uri.to_uri().map_err(|e| e.to_string())?, + query: vec![ExtensionQuery { + extension_kind: EnumOrUnknown::new(kind), + ..Default::default() + }], + ..Default::default() + }], + ..Default::default() + }; + let body = request.write_to_bytes().map_err(|e| e.to_string())?; + let bytes = sp_request( + session, + token, + Method::POST, + "/extended-metadata/v0/extended-metadata", + Some("application/x-protobuf"), + &body, + ) + .await?; + let mut response = + BatchedExtensionResponse::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + let mut metadata = response + .extended_metadata + .pop() + .ok_or_else(|| SpClientError::ExpectedEntry("extended_metadata").to_string())?; + let mut data = metadata + .extension_data + .pop() + .ok_or_else(|| SpClientError::ExpectedEntry("extension_data").to_string())?; + data.extension_data + .take() + .map(|data| Bytes::from(data.value)) + .ok_or_else(|| SpClientError::ExpectedEntry("data").to_string()) +} + +async fn track_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { + let bytes = metadata_bytes(session, token, ExtensionKind::TRACK_V4, uri).await?; + let message = + librespot_protocol::metadata::Track::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + Track::parse(&message, uri).map_err(|e| e.to_string()) +} + +async fn album_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { + let bytes = metadata_bytes(session, token, ExtensionKind::ALBUM_V4, uri).await?; + let message = + librespot_protocol::metadata::Album::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + Album::parse(&message, uri).map_err(|e| e.to_string()) +} + +async fn artist_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { + let bytes = metadata_bytes(session, token, ExtensionKind::ARTIST_V4, uri).await?; + let message = librespot_protocol::metadata::Artist::parse_from_bytes(&bytes) + .map_err(|e| e.to_string())?; + Artist::parse(&message, uri).map_err(|e| e.to_string()) +} + +async fn show_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { + let bytes = metadata_bytes(session, token, ExtensionKind::SHOW_V4, uri).await?; + let message = + librespot_protocol::metadata::Show::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + Show::parse(&message, uri).map_err(|e| e.to_string()) +} + +async fn episode_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { + let bytes = metadata_bytes(session, token, ExtensionKind::EPISODE_V4, uri).await?; + let message = librespot_protocol::metadata::Episode::parse_from_bytes(&bytes) + .map_err(|e| e.to_string())?; + Episode::parse(&message, uri).map_err(|e| e.to_string()) +} + +async fn playlist_get( + session: &Session, + token: &str, + uri: &SpotifyUri, +) -> Result { + let id = uri.to_id().map_err(|e| e.to_string())?; + let bytes = sp_request( + session, + token, + Method::GET, + &format!("/playlist/v2/playlist/{id}"), + None, + &[], + ) + .await?; + let content = SelectedListContent::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + Playlist::parse(&content, uri).map_err(|e| e.to_string()) +} + +async fn context_get(session: &Session, token: &str, uri: &str) -> Result { + let bytes = sp_request( + session, + token, + Method::GET, + &format!("/context-resolve/v1/{uri}"), + None, + &[], + ) + .await?; + let text = String::from_utf8(bytes.to_vec()).map_err(|e| e.to_string())?; + protobuf_json_mapping::parse_from_str::(&text).map_err(|e| e.to_string()) +} + +async fn fetch_tracks( + session: &Session, + token: &str, + uris: &[SpotifyUri], + limit: usize, +) -> Vec { + let futures = uris.iter().take(limit).map(|uri| { + let session = session.clone(); + let token = token.to_string(); + let uri = uri.clone(); + async move { + track_get(&session, &token, &uri) + .await + .ok() + .map(|track| track_json(&track)) + } + }); + join_all(futures).await.into_iter().flatten().collect() +} + +async fn fetch_albums( + session: &Session, + token: &str, + uris: &[SpotifyUri], + with_tracks: bool, +) -> Vec { + let futures = uris.iter().map(|uri| { + let session = session.clone(); + let token = token.to_string(); + let uri = uri.clone(); + async move { album_get(&session, &token, &uri).await.ok() } + }); + let mut albums = Vec::new(); + for album in join_all(futures).await.into_iter().flatten() { + let tracks = if with_tracks { + let uris: Vec = album.tracks().cloned().collect(); + let fetched = fetch_tracks(session, token, &uris, 100).await; + fetched.into_iter().collect() + } else { + Vec::new() + }; + albums.push(album_json(&album, tracks)); + } + albums +} + +fn encode(input: &str) -> String { + let mut out = String::with_capacity(input.len() * 2); + for byte in input.bytes() { + match byte { + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => { + out.push(byte as char) + } + _ => { + out.push('%'); + out.push(char::from(b"0123456789ABCDEF"[(byte >> 4) as usize])); + out.push(char::from(b"0123456789ABCDEF"[(byte & 0x0F) as usize])); + } + } + } + out +} + +fn pf_str(node: &Value, pointer: &str) -> String { + node.pointer(pointer) + .and_then(Value::as_str) + .unwrap_or_default() + .to_string() +} + +fn pf_image(node: Option<&Value>) -> Option { + let sources = node?.as_array()?; + let mut best: Option<(i64, String)> = None; + for source in sources { + let Some(url) = source.get("url").and_then(Value::as_str) else { + continue; + }; + let width = source.get("width").and_then(Value::as_i64).unwrap_or(0); + if width >= 200 + && best + .as_ref() + .is_none_or(|(best_width, _)| width < *best_width) + { + best = Some((width, url.to_string())); + } + } + best.map(|(_, url)| url).or_else(|| { + sources.iter().find_map(|source| { + source + .get("url") + .and_then(Value::as_str) + .map(str::to_string) + }) + }) +} + +fn pf_artists(data: &Value) -> (String, String) { + let artist = data.pointer("/artists/items/0"); + let name = artist + .map(|a| pf_str(a, "/profile/name")) + .unwrap_or_default(); + let id = artist.map(|a| pf_str(a, "/uri")).unwrap_or_default(); + ( + name, + if id.is_empty() { + String::new() + } else { + id.rsplit(':').next().unwrap_or_default().to_string() + }, + ) +} + +fn pf_track(data: &Value) -> Option { + let uri = data.get("uri")?.as_str()?.to_string(); + let id = uri.rsplit(':').next().unwrap_or_default().to_string(); + let (artist, artist_id) = pf_artists(data); + let album_uri = pf_str(data, "/albumOfTrack/uri"); + Some(TrackJson { + id, + title: pf_str(data, "/name"), + artist: if artist.is_empty() { + "Unknown artist".to_string() + } else { + artist + }, + artist_id, + album_id: album_uri.rsplit(':').next().unwrap_or_default().to_string(), + art_res: 0, + duration_ms: data + .pointer("/duration/totalMilliseconds") + .and_then(Value::as_i64) + .unwrap_or(0), + explicit: pf_str(data, "/contentRating/label") == "EXPLICIT", + playable: true, + uri, + art_url: pf_image(data.pointer("/albumOfTrack/coverArt/sources")), + }) +} + +fn pf_album(data: &Value) -> Option { + let uri = data.get("uri")?.as_str()?; + let (artist, artist_id) = pf_artists(data); + Some(AlbumJson { + id: uri.rsplit(':').next().unwrap_or_default().to_string(), + title: pf_str(data, "/name"), + artist: if artist.is_empty() { + "Unknown artist".to_string() + } else { + artist + }, + artist_id, + year: pf_str(data, "/date/year").parse().unwrap_or(0), + art_res: 0, + tracks: Vec::new(), + art_url: pf_image(data.pointer("/coverArt/sources")), + track_count: data + .pointer("/tracks/totalCount") + .and_then(Value::as_i64) + .unwrap_or(0) as i32, + }) +} + +fn pf_artist(data: &Value) -> Option { + let uri = data.get("uri")?.as_str()?; + Some(ArtistJson { + id: uri.rsplit(':').next().unwrap_or_default().to_string(), + name: pf_str(data, "/profile/name"), + art_res: 0, + album_ids: Vec::new(), + album_count: 0, + art_url: pf_image(data.pointer("/visuals/avatarImage/sources")), + }) +} + +fn pf_playlist(data: &Value) -> Option { + let uri = data.get("uri")?.as_str()?; + let owner = data.pointer("/ownerV2/data"); + Some(PlaylistJson { + id: uri.rsplit(':').next().unwrap_or_default().to_string(), + name: pf_str(data, "/name"), + owner: owner + .map(|o| pf_str(o, "/name")) + .unwrap_or_else(|| "Spotify".to_string()), + owner_id: owner + .and_then(|o| o.get("uri")) + .and_then(Value::as_str) + .map(|u| u.rsplit(':').next().unwrap_or_default().to_string()), + art_res: 0, + tracks: Vec::new(), + art_url: pf_image(data.pointer("/images/items/0/sources")), + track_count: data + .pointer("/tracks/totalCount") + .and_then(Value::as_i64) + .unwrap_or(0) as i32, + }) +} + +fn pf_show(data: &Value) -> Option { + let uri = data.get("uri")?.as_str()?; + Some(ShowJson { + id: uri.rsplit(':').next().unwrap_or_default().to_string(), + name: pf_str(data, "/name"), + publisher: if pf_str(data, "/publisher/name").is_empty() { + "Podcast".to_string() + } else { + pf_str(data, "/publisher/name") + }, + art_res: 0, + episodes: Vec::new(), + art_url: pf_image(data.pointer("/coverArt/sources")), + episode_count: data + .pointer("/episodes/totalCount") + .and_then(Value::as_i64) + .unwrap_or(0) as i32, + }) +} + +fn section_items<'a>(root: &'a Value, name: &str) -> &'a [Value] { + root.pointer(&format!("/data/searchV2/{name}/items")) + .and_then(Value::as_array) + .map(Vec::as_slice) + .unwrap_or(&[]) +} + +async fn pathfinder_search( + session: &Session, + token: &str, + query: &str, + limit: i32, +) -> Result { + let variables = json!({ + "searchTerm": query, + "offset": 0, + "limit": limit, + "numberOfTopResults": 5, + "includeAudiobooks": false, + }); + let extensions = json!({ + "persistedQuery": {"version": 1, "sha256Hash": SEARCH_QUERY_HASH}, + }); + let url = format!( + "{PATHFINDER_BASE}/pathfinder/v1/query?operationName=searchDesktop&variables={}&extensions={}", + encode(&variables.to_string()), + encode(&extensions.to_string()) + ); + let request = http::Request::builder() + .method(Method::GET) + .uri(url) + .header(AUTHORIZATION, format!("Bearer {token}")) + .header(ACCEPT, HeaderValue::from_static("application/json")) + .header("app-platform", HeaderValue::from_static("WebPlayer")) + .body(Bytes::new()) + .map_err(|e| format!("request: {e}"))?; + let body = session + .http_client() + .request_body(request) + .await + .map_err(|e| format!("pathfinder: {e}"))?; + let value: Value = serde_json::from_slice(&body).map_err(|e| format!("json: {e}"))?; + if let Some(errors) = value.get("errors") { + return Err(format!("pathfinder error: {errors}")); + } + if value.pointer("/data/searchV2").is_none() { + return Err("pathfinder returned no searchV2".to_string()); + } + Ok(value) +} + +pub async fn search( + session: &Session, + token: &str, + query: &str, + limit: i32, + scopes: &[String], +) -> Result { + let want = |scope: &str| { + scopes.is_empty() || scopes.iter().any(|value| value.eq_ignore_ascii_case(scope)) + }; + match pathfinder_search(session, token, query, limit.clamp(1, 50)).await { + Ok(value) => { + let tracks = if want("Tracks") { + section_items(&value, "tracksV2") + .iter() + .filter_map(|item| item.pointer("/item/data")) + .filter_map(pf_track) + .collect() + } else { + Vec::new() + }; + let artists = if want("Artists") { + section_items(&value, "artists") + .iter() + .filter_map(|item| item.get("data")) + .filter_map(pf_artist) + .collect() + } else { + Vec::new() + }; + let albums = if want("Albums") { + section_items(&value, "albumsV2") + .iter() + .filter_map(|item| item.get("data")) + .filter_map(pf_album) + .collect() + } else { + Vec::new() + }; + let playlists = if want("Playlists") { + section_items(&value, "playlists") + .iter() + .filter_map(|item| item.get("data")) + .filter_map(pf_playlist) + .collect() + } else { + Vec::new() + }; + let shows = if want("Shows") { + section_items(&value, "podcasts") + .iter() + .filter_map(|item| item.get("data")) + .filter_map(pf_show) + .collect() + } else { + Vec::new() + }; + let results = SearchJson { + tracks, + artists, + albums, + playlists, + shows, + }; + Ok(serde_json::to_string(&results).map_err(|e| e.to_string())?) + } + Err(error) => { + log::warn!("Pathfinder search failed, using context fallback: {error}"); + let context_uri = format!("spotify:search:{}", query.replace(' ', "+")); + let context = context_get(session, token, &context_uri) + .await + .map_err(|e| format!("search fallback: {e}"))?; + let mut uris = Vec::new(); + let mut seen = HashSet::new(); + for page in context.pages.iter() { + for track in page.tracks.iter() { + let uri = track.uri(); + if uri.starts_with("spotify:track:") && seen.insert(uri.to_string()) { + uris.push(SpotifyUri::from_uri(uri).map_err(|e| e.to_string())?); + } + } + } + let tracks = fetch_tracks(session, token, &uris, limit.clamp(1, 50) as usize).await; + let results = SearchJson { + tracks, + artists: Vec::new(), + albums: Vec::new(), + playlists: Vec::new(), + shows: Vec::new(), + }; + Ok(serde_json::to_string(&results).map_err(|e| e.to_string())?) + } + } +} + +#[derive(Debug, Clone)] +struct CollectionItem { + uri: String, + added_at: i32, + is_removed: bool, +} + +fn put_varint(out: &mut Vec, mut value: u64) { + while value >= 0x80 { + out.push((value as u8) | 0x80); + value >>= 7; + } + out.push(value as u8); +} + +fn put_string(out: &mut Vec, field: u32, value: &str) { + put_varint(out, ((field << 3) | 2) as u64); + put_varint(out, value.len() as u64); + out.extend_from_slice(value.as_bytes()); +} + +fn read_varint(bytes: &[u8], index: &mut usize) -> Option { + let mut value = 0u64; + let mut shift = 0; + while *index < bytes.len() { + let byte = bytes[*index]; + *index += 1; + value |= ((byte & 0x7f) as u64) << shift; + if byte & 0x80 == 0 { + return Some(value); + } + shift += 7; + } + None +} + +fn parse_collection_item(bytes: &[u8]) -> CollectionItem { + let mut item = CollectionItem { + uri: String::new(), + added_at: 0, + is_removed: false, + }; + let mut index = 0; + while index < bytes.len() { + let Some(key) = read_varint(bytes, &mut index) else { + break; + }; + let field = (key >> 3) as u32; + let wire = (key & 7) as u8; + match (field, wire) { + (1, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + let end = index + len as usize; + if end > bytes.len() { + break; + } + item.uri = String::from_utf8_lossy(&bytes[index..end]).into_owned(); + index = end; + } + (2, 0) => { + let Some(value) = read_varint(bytes, &mut index) else { + break; + }; + item.added_at = value as i32; + } + (3, 0) => { + let Some(value) = read_varint(bytes, &mut index) else { + break; + }; + item.is_removed = value != 0; + } + (_, 0) => { + let _ = read_varint(bytes, &mut index); + } + (_, 1) => { + index += 8; + } + (_, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + index += len as usize; + } + (_, 5) => { + index += 4; + } + _ => break, + } + } + item +} + +fn parse_collection_page(bytes: &[u8]) -> (Vec, String) { + let mut items = Vec::new(); + let mut next_page_token = String::new(); + let mut index = 0; + while index < bytes.len() { + let Some(key) = read_varint(bytes, &mut index) else { + break; + }; + let field = (key >> 3) as u32; + let wire = (key & 7) as u8; + match (field, wire) { + (1, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + let end = index + len as usize; + if end > bytes.len() { + break; + } + items.push(parse_collection_item(&bytes[index..end])); + index = end; + } + (2, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + let end = index + len as usize; + if end > bytes.len() { + break; + } + next_page_token = String::from_utf8_lossy(&bytes[index..end]).into_owned(); + index = end; + } + (_, 0) => { + let _ = read_varint(bytes, &mut index); + } + (_, 1) => { + index += 8; + } + (_, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + index += len as usize; + } + (_, 5) => { + index += 4; + } + _ => break, + } + } + (items, next_page_token) +} + +async fn collection_page( + session: &Session, + token: &str, + set: &str, + limit: usize, +) -> Result, String> { + let username = session.username(); + let mut items = Vec::new(); + let mut page_token = String::new(); + let mut seen = HashSet::new(); + for _ in 0..COLLECTION_PAGES { + let mut body = Vec::new(); + put_string(&mut body, 1, &username); + put_string(&mut body, 2, set); + put_string(&mut body, 3, &page_token); + put_varint(&mut body, (4 << 3) as u64); + put_varint(&mut body, COLLECTION_PAGE as u64); + let bytes = sp_request( + session, + token, + Method::POST, + "/collection/v2/paging", + Some("application/vnd.collection-v2.spotify.proto"), + &body, + ) + .await + .map_err(|e| format!("collection {set}: {e}"))?; + let (page_items, next) = parse_collection_page(&bytes); + let got = page_items.len(); + for item in page_items.into_iter().filter(|item| !item.is_removed) { + items.push(item); + if items.len() >= limit { + return Ok(items); + } + } + if next.is_empty() || got == 0 || !seen.insert(next.clone()) { + break; + } + page_token = next; + } + Ok(items) +} + +fn collection_uris(items: &[CollectionItem], prefix: &str) -> Vec { + items + .iter() + .filter(|item| item.uri.starts_with(prefix)) + .filter_map(|item| SpotifyUri::from_uri(&item.uri).ok()) + .collect() +} + +async fn rootlist_playlists( + session: &Session, + token: &str, + limit: usize, +) -> Result, String> { + let mut seen = 0usize; + let mut playlists = Vec::new(); + for _ in 0..COLLECTION_PAGES { + let user = session.username(); + let endpoint = format!( + "/playlist/v2/user/{user}/rootlist?decorate=revision,attributes,length,owner,capabilities,status_code&from={seen}&length=500" + ); + let bytes = sp_request(session, token, Method::GET, &endpoint, None, &[]) + .await + .map_err(|e| format!("rootlist: {e}"))?; + let content = SelectedListContent::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; + let items = &content.contents.items; + let meta = &content.contents.meta_items; + if items.is_empty() { + break; + } + if content.contents.pos() as usize != seen { + break; + } + for (index, item) in items.iter().enumerate() { + let uri = item.uri(); + if !uri.starts_with("spotify:playlist:") { + continue; + } + let metadata = meta.get(index); + let attributes = metadata.and_then(|m| m.attributes.as_ref()); + let name = attributes.map(|a| a.name().to_string()).unwrap_or_default(); + // A few stale rootlist entries carry no name; they cannot be navigated usefully. + if name.trim().is_empty() { + continue; + } + let owner = metadata + .map(|m| m.owner_username().to_string()) + .unwrap_or_default(); + let art_url = attributes + .and_then(|a| a.picture_size.iter().find(|size| !size.url().is_empty())) + .map(|size| size.url().to_string()); + let owner_id = if owner.is_empty() { None } else { Some(owner.clone()) }; + let owner = if owner.is_empty() { + "Spotify".to_string() + } else if owner == user { + "You".to_string() + } else { + owner + }; + playlists.push(PlaylistJson { + id: uri.rsplit(':').next().unwrap_or_default().to_string(), + name, + owner, + owner_id, + art_res: 0, + tracks: Vec::new(), + art_url, + track_count: metadata.map(|m| m.length()).unwrap_or(0), + }); + if playlists.len() >= limit { + return Ok(playlists); + } + } + seen += items.len(); + if seen >= content.length().max(0) as usize { + break; + } + } + Ok(playlists) +} + +pub async fn library(session: &Session, token: &str, filter: &str) -> Result { + match filter { + "LikedSongs" => { + let items = collection_page(session, token, "collection", 50).await?; + let uris = collection_uris(&items, "spotify:track:"); + let tracks = fetch_tracks(session, token, &uris, 50).await; + Ok(serde_json::to_string(&tracks).map_err(|e| e.to_string())?) + } + "Playlists" => Ok( + serde_json::to_string(&rootlist_playlists(session, token, 200).await?) + .map_err(|e| e.to_string())?, + ), + "Albums" => { + let items = collection_page(session, token, "collection", COLLECTION_LIMIT).await?; + let uris = collection_uris(&items, "spotify:album:"); + let albums = fetch_albums(session, token, &uris, false).await; + Ok(serde_json::to_string(&albums).map_err(|e| e.to_string())?) + } + "Artists" => { + let mut items = collection_page(session, token, "artist", COLLECTION_LIMIT).await?; + if items.is_empty() { + items = collection_page(session, token, "collection", COLLECTION_LIMIT) + .await? + .into_iter() + .filter(|item| item.uri.starts_with("spotify:artist:")) + .collect(); + } + let uris = collection_uris(&items, "spotify:artist:"); + let futures = uris.iter().map(|uri| { + let session = session.clone(); + let uri = uri.clone(); + async move { + Artist::get(&session, &uri) + .await + .ok() + .map(|artist| artist_json(&artist, Vec::new())) + } + }); + let artists: Vec = join_all(futures).await.into_iter().flatten().collect(); + Ok(serde_json::to_string(&artists).map_err(|e| e.to_string())?) + } + "Shows" => { + let items = collection_page(session, token, "show", COLLECTION_LIMIT).await?; + let uris = collection_uris(&items, "spotify:show:"); + let futures = uris.iter().map(|uri| { + let session = session.clone(); + let uri = uri.clone(); + async move { + Show::get(&session, &uri) + .await + .ok() + .map(|show| show_json(&show, Vec::new())) + } + }); + let shows: Vec = join_all(futures).await.into_iter().flatten().collect(); + Ok(serde_json::to_string(&shows).map_err(|e| e.to_string())?) + } + other => Err(format!("unknown library filter: {other}")), + } +} + +pub async fn collection( + session: &Session, + token: &str, + kind: &str, + id: &str, +) -> Result { + match kind { + "Album" => { + let album = album_get(session, token, &spotify_uri(id, "album")?) + .await + .map_err(|e| e.to_string())?; + let uris: Vec = album.tracks().cloned().collect(); + let tracks = fetch_tracks(session, token, &uris, 100).await; + let album_json = album_json(&album, tracks); + let subtitle = format!( + "{} · {} · {} tracks", + album_json.artist, album_json.year, album_json.track_count + ); + let collection = CollectionJson { + kind: "Album".to_string(), + id: album_json.id.clone(), + title: album_json.title.clone(), + subtitle, + art_res: 0, + items: album_json + .tracks + .iter() + .map(|track| serde_json::to_value(track).unwrap_or(Value::Null)) + .collect(), + artist_id: Some(album_json.artist_id.clone()).filter(|value| !value.is_empty()), + art_url: album_json.art_url.clone(), + }; + Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) + } + "Playlist" => { + let playlist = playlist_get(session, token, &spotify_uri(id, "playlist")?) + .await + .map_err(|e| e.to_string())?; + let uris: Vec = playlist.tracks().cloned().collect(); + let tracks = fetch_tracks(session, token, &uris, PLAYLIST_TRACK_LIMIT).await; + let mut playlist_json = playlist_json(&playlist, tracks); + if playlist_json.owner == session.username() { + playlist_json.owner = "You".to_string(); + } + let subtitle = format!( + "{} · {} tracks", + playlist_json.owner, playlist_json.track_count + ); + let collection = CollectionJson { + kind: "Playlist".to_string(), + id: playlist_json.id.clone(), + title: playlist_json.name.clone(), + subtitle, + art_res: 0, + items: playlist_json + .tracks + .iter() + .map(|track| serde_json::to_value(track).unwrap_or(Value::Null)) + .collect(), + artist_id: playlist_json.owner_id.clone(), + art_url: playlist_json.art_url.clone(), + }; + Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) + } + "Show" => { + let show = show_get(session, token, &spotify_uri(id, "show")?) + .await + .map_err(|e| e.to_string())?; + let uris: Vec = show.episodes.iter().cloned().collect(); + let futures = uris.iter().take(SHOW_EPISODE_LIMIT).map(|uri| { + let session = session.clone(); + let token = token.to_string(); + let uri = uri.clone(); + let show = show.clone(); + async move { + episode_get(&session, &token, &uri) + .await + .ok() + .map(|episode| episode_json(&episode, &show)) + } + }); + let episodes: Vec = + join_all(futures).await.into_iter().flatten().collect(); + let show_json = show_json(&show, episodes); + let subtitle = format!( + "{} · {} episodes", + show_json.publisher, show_json.episode_count + ); + let collection = CollectionJson { + kind: "Show".to_string(), + id: show_json.id.clone(), + title: show_json.name.clone(), + subtitle, + art_res: 0, + items: show_json + .episodes + .iter() + .map(|episode| serde_json::to_value(episode).unwrap_or(Value::Null)) + .collect(), + artist_id: None, + art_url: show_json.art_url.clone(), + }; + Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) + } + other => Err(format!("unknown collection type: {other}")), + } +} + +pub async fn artist(session: &Session, token: &str, id: &str) -> Result { + let artist = artist_get(session, token, &spotify_uri(id, "artist")?) + .await + .map_err(|e| e.to_string())?; + let album_ids: Vec = artist + .albums_current() + .take(ARTIST_ALBUM_LIMIT) + .chain(artist.singles_current().take(ARTIST_ALBUM_LIMIT)) + .map(|uri| uri_of(&id_of(uri), "album")) + .collect(); + Ok(serde_json::to_string(&artist_json(&artist, album_ids)).map_err(|e| e.to_string())?) +} + +pub async fn albums_by(session: &Session, token: &str, id: &str) -> Result { + let artist = artist_get(session, token, &spotify_uri(id, "artist")?) + .await + .map_err(|e| e.to_string())?; + let uris: Vec = artist + .albums_current() + .take(ARTIST_ALBUM_LIMIT) + .chain(artist.singles_current().take(ARTIST_ALBUM_LIMIT)) + .cloned() + .collect(); + let mut albums = fetch_albums(session, token, &uris, false).await; + albums.sort_by(|a, b| b.year.cmp(&a.year).then_with(|| a.title.cmp(&b.title))); + Ok(serde_json::to_string(&albums).map_err(|e| e.to_string())?) +} + +pub async fn top_tracks(session: &Session, token: &str, id: &str) -> Result { + let artist = artist_get(session, token, &spotify_uri(id, "artist")?) + .await + .map_err(|e| e.to_string())?; + let country = session.country(); + let uris: Vec = artist + .top_tracks + .for_country(&country) + .iter() + .take(ARTIST_TOP_LIMIT) + .cloned() + .collect(); + let tracks = fetch_tracks(session, token, &uris, ARTIST_TOP_LIMIT).await; + Ok(serde_json::to_string(&tracks).map_err(|e| e.to_string())?) +} diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index b913de44..22a679ec 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -2,11 +2,13 @@ use crate::audio::{PcmSink, CHANNELS, SAMPLE_RATE}; use librespot_core::authentication::Credentials; +use librespot_core::config::SessionConfig; use librespot_core::session::Session; use librespot_core::SpotifyUri; use librespot_playback::config::{Bitrate, PlayerConfig}; use librespot_playback::mixer::VolumeGetter; use librespot_playback::player::{Player, PlayerEventChannel}; +use std::path::PathBuf; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; use std::sync::{Arc, Mutex, OnceLock}; @@ -25,6 +27,7 @@ impl VolumeGetter for SharedVolume { struct State { session: Option, + access_token: Option, player: Option>, events: Option, volume: Arc, @@ -34,6 +37,7 @@ impl Default for State { fn default() -> Self { Self { session: None, + access_token: None, player: None, events: None, volume: Arc::new(Volume(AtomicU16::new(65_535))), @@ -45,7 +49,7 @@ static RUNTIME: OnceLock = OnceLock::new(); static STATE: OnceLock> = OnceLock::new(); static AUDIO_RX: OnceLock>>> = OnceLock::new(); -fn runtime() -> &'static tokio::runtime::Runtime { +pub(crate) fn runtime() -> &'static tokio::runtime::Runtime { RUNTIME.get_or_init(|| { tokio::runtime::Builder::new_multi_thread() .enable_all() @@ -68,6 +72,21 @@ fn audio_rx() -> &'static Mutex>> { } static AUDIO_TX: OnceLock>> = OnceLock::new(); +static TMP_DIR: OnceLock>> = OnceLock::new(); + +pub fn set_tmp_dir(path: &str) { + let slot = TMP_DIR.get_or_init(|| Mutex::new(None)); + if let Ok(mut guard) = slot.lock() { + *guard = Some(PathBuf::from(path)); + } +} + +fn tmp_dir() -> PathBuf { + TMP_DIR + .get() + .and_then(|slot| slot.lock().ok().and_then(|guard| guard.clone())) + .unwrap_or_else(std::env::temp_dir) +} pub fn init() -> Result<(), String> { // Force the audio channel (and its sender) to exist before the player starts. @@ -75,16 +94,57 @@ pub fn init() -> Result<(), String> { Ok(()) } +pub(crate) fn run(future: F) -> F::Output { + runtime().block_on(future) +} + +pub(crate) fn with_session_and_token( + f: impl FnOnce(&Session, &str) -> Result, +) -> Result { + // Clone out of the lock: catalog calls may make several network round trips, and the audio + // event poller must never wait behind them. + let (session, token) = { + let guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + let session = guard + .session + .clone() + .ok_or_else(|| "SESSION_NOT_CONNECTED".to_string())?; + let token = guard + .access_token + .clone() + .ok_or_else(|| "SESSION_NOT_CONNECTED".to_string())?; + (session, token) + }; + f(&session, &token) +} + pub fn connect(access_token: &str) -> Result<(), String> { disconnect(); let rt = runtime(); let _guard = rt.enter(); - let session = Session::new(Default::default(), None); - rt.block_on(session.connect(Credentials::with_access_token(access_token), false)) + // The OAuth token comes from the desktop/keymaster client. librespot defaults to the Android + // client id on this target, which would make Login5 reject the stored credential. + let config = SessionConfig { + client_id: "65b708073fc0480ea92a077233ca87bd".to_string(), + tmp_dir: tmp_dir(), + ..Default::default() + }; + let session = Session::new(config, None); + rt.block_on(session.connect(Credentials::with_access_token(access_token), true)) .map_err(|e| format!("SESSION_CONNECT_FAILED: {e}"))?; let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; guard.session = Some(session); + guard.access_token = Some(access_token.to_string()); + Ok(()) +} + +pub fn set_access_token(access_token: &str) -> Result<(), String> { + let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; + if guard.session.is_none() { + return Err("SESSION_NOT_CONNECTED".to_string()); + } + guard.access_token = Some(access_token.to_string()); Ok(()) } @@ -94,6 +154,7 @@ pub fn disconnect() { player.stop(); } guard.events = None; + guard.access_token = None; if let Some(session) = guard.session.take() { session.shutdown(); } diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index ed5cc623..36a29420 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -1,6 +1,7 @@ //! JNI surface for Sidetracks. All state lives in `engine`; these functions only translate. mod audio; +mod catalog; mod engine; use jni::objects::{JObject, JString}; @@ -12,6 +13,16 @@ fn string(env: &mut JNIEnv, value: &JString) -> Option { env.get_string(value).ok().map(Into::into) } +fn catalog_result(env: &mut JNIEnv, result: Result) -> jni::sys::jstring { + let value = match result { + Ok(value) => value, + Err(error) => serde_json::json!({ "error": error }).to_string(), + }; + env.new_string(value) + .map(|value| value.into_raw()) + .unwrap_or(std::ptr::null_mut()) +} + #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeInit( _env: JNIEnv, @@ -28,6 +39,17 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB } } +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSetTmpDir( + mut env: JNIEnv, + _this: JObject, + path: JString, +) { + if let Some(path) = string(&mut env, &path) { + engine::set_tmp_dir(&path); + } +} + #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeConnect( mut env: JNIEnv, @@ -40,6 +62,18 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB } } +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSetAccessToken( + mut env: JNIEnv, + _this: JObject, + token: JString, +) -> jni::sys::jboolean { + let Some(token) = string(&mut env, &token) else { + return JNI_FALSE; + }; + bool_result(engine::set_access_token(&token)) +} + #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeDisconnect( _env: JNIEnv, @@ -159,6 +193,106 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB } } +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogLibrary( + mut env: JNIEnv, + _this: JObject, + filter: JString, +) -> jni::sys::jstring { + let Some(filter) = string(&mut env, &filter) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::library(session, token, &filter)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogCollection( + mut env: JNIEnv, + _this: JObject, + kind: JString, + id: JString, +) -> jni::sys::jstring { + let (Some(kind), Some(id)) = (string(&mut env, &kind), string(&mut env, &id)) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::collection(session, token, &kind, &id)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogArtist( + mut env: JNIEnv, + _this: JObject, + id: JString, +) -> jni::sys::jstring { + let Some(id) = string(&mut env, &id) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::artist(session, token, &id)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogAlbumsBy( + mut env: JNIEnv, + _this: JObject, + id: JString, +) -> jni::sys::jstring { + let Some(id) = string(&mut env, &id) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::albums_by(session, token, &id)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogTopTracks( + mut env: JNIEnv, + _this: JObject, + id: JString, +) -> jni::sys::jstring { + let Some(id) = string(&mut env, &id) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::top_tracks(session, token, &id)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCatalogSearch( + mut env: JNIEnv, + _this: JObject, + query: JString, + scopes: JString, + limit: jlong, +) -> jni::sys::jstring { + let (Some(query), Some(scopes)) = (string(&mut env, &query), string(&mut env, &scopes)) else { + return std::ptr::null_mut(); + }; + let scopes: Vec = serde_json::from_str(&scopes).unwrap_or_default(); + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::search( + session, + token, + &query, + limit as i32, + &scopes, + )) + }); + catalog_result(&mut env, result) +} + fn bool_result(result: Result<(), String>) -> jboolean { if result.is_ok() { JNI_TRUE diff --git a/native/vendor/librespot-core/Cargo.toml b/native/vendor/librespot-core/Cargo.toml new file mode 100644 index 00000000..4de310c1 --- /dev/null +++ b/native/vendor/librespot-core/Cargo.toml @@ -0,0 +1,275 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.85" +name = "librespot-core" +version = "0.8.0" +authors = ["Paul Lietar "] +build = "build.rs" +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "The core functionality provided by librespot" +readme = false +license = "MIT" +repository = "https://github.com/librespot-org/librespot" + +[features] +__rustls = [] +default = ["native-tls"] +native-tls = [ + "dep:hyper-tls", + "hyper-proxy2/tls", + "librespot-oauth/native-tls", + "tokio-tungstenite/native-tls", +] +rustls-tls-native-roots = [ + "__rustls", + "hyper-proxy2/rustls", + "hyper-rustls/native-tokio", + "librespot-oauth/rustls-tls-native-roots", + "tokio-tungstenite/rustls-tls-native-roots", +] +rustls-tls-webpki-roots = [ + "__rustls", + "hyper-proxy2/rustls-webpki", + "hyper-rustls/webpki-tokio", + "librespot-oauth/rustls-tls-webpki-roots", + "tokio-tungstenite/rustls-tls-webpki-roots", +] + +[lib] +name = "librespot_core" +path = "src/lib.rs" + +[[test]] +name = "connect" +path = "tests/connect.rs" + +[dependencies.aes] +version = "0.8" + +[dependencies.base64] +version = "0.22" + +[dependencies.byteorder] +version = "1.5" + +[dependencies.bytes] +version = "1" + +[dependencies.data-encoding] +version = "2.9" + +[dependencies.flate2] +version = "1.1" + +[dependencies.form_urlencoded] +version = "1.2" + +[dependencies.futures-core] +version = "0.3" + +[dependencies.futures-util] +version = "0.3" +features = [ + "alloc", + "bilock", + "unstable", +] +default-features = false + +[dependencies.governor] +version = "0.10" +features = ["std"] +default-features = false + +[dependencies.hmac] +version = "0.12" + +[dependencies.http] +version = "1.3" + +[dependencies.http-body-util] +version = "0.1" + +[dependencies.httparse] +version = "1.10" + +[dependencies.hyper] +version = "1.6" +features = [ + "http1", + "http2", +] + +[dependencies.hyper-proxy2] +version = "0.1" +default-features = false + +[dependencies.hyper-rustls] +version = "0.27" +features = [ + "http1", + "http2", + "ring", +] +optional = true +default-features = false + +[dependencies.hyper-tls] +version = "0.6" +optional = true + +[dependencies.hyper-util] +version = "0.1" +features = [ + "client", + "http1", + "http2", +] +default-features = false + +[dependencies.librespot-oauth] +version = "0.8.0" +default-features = false + +[dependencies.librespot-protocol] +version = "0.8.0" +default-features = false + +[dependencies.log] +version = "0.4" + +[dependencies.nonzero_ext] +version = "0.3" + +[dependencies.num-bigint] +version = "0.4" + +[dependencies.num-derive] +version = "0.4" + +[dependencies.num-integer] +version = "0.1" + +[dependencies.num-traits] +version = "0.2" + +[dependencies.pbkdf2] +version = "0.12" +features = ["hmac"] +default-features = false + +[dependencies.pin-project-lite] +version = "0.2" + +[dependencies.priority-queue] +version = "2.5" + +[dependencies.protobuf] +version = "3.7" + +[dependencies.protobuf-json-mapping] +version = "3.7" + +[dependencies.quick-xml] +version = "0.38" +features = ["serialize"] + +[dependencies.rand] +version = "0.9" +features = ["thread_rng"] +default-features = false + +[dependencies.rsa] +version = "0.9" + +[dependencies.serde] +version = "1.0" +features = ["derive"] + +[dependencies.serde_json] +version = "1.0" + +[dependencies.sha1] +version = "0.10" +features = ["oid"] + +[dependencies.shannon] +version = "0.2" + +[dependencies.sysinfo] +version = "0.36" +features = ["system"] +default-features = false + +[dependencies.thiserror] +version = "2" + +[dependencies.time] +version = "0.3" +features = [ + "formatting", + "parsing", +] + +[dependencies.tokio] +version = "1" +features = [ + "io-util", + "macros", + "net", + "rt", + "sync", + "time", +] + +[dependencies.tokio-stream] +version = "0.1" +default-features = false + +[dependencies.tokio-tungstenite] +version = "0.28" +default-features = false + +[dependencies.tokio-util] +version = "0.7" +default-features = false + +[dependencies.url] +version = "2" + +[dependencies.uuid] +version = "1" +features = ["v4"] +default-features = false + +[dev-dependencies.tokio] +version = "1" +features = ["macros"] + +[build-dependencies.rand] +version = "0.9" +features = ["thread_rng"] +default-features = false + +[build-dependencies.rand_distr] +version = "0.5" + +[build-dependencies.vergen-gitcl] +version = "1.0" +features = ["build"] +default-features = false diff --git a/native/vendor/librespot-core/Cargo.toml.orig b/native/vendor/librespot-core/Cargo.toml.orig new file mode 100644 index 00000000..ffe7c108 --- /dev/null +++ b/native/vendor/librespot-core/Cargo.toml.orig @@ -0,0 +1,120 @@ +[package] +name = "librespot-core" +version = "0.8.0" +rust-version.workspace = true +authors = ["Paul Lietar "] +license.workspace = true +description = "The core functionality provided by librespot" +repository.workspace = true +edition.workspace = true +build = "build.rs" + +[features] +# Refer to the workspace Cargo.toml for the list of features +default = ["native-tls"] + +# TLS backends (mutually exclusive - see oauth/src/lib.rs for compile-time checks) +# Note: Validation is in oauth since it's compiled first in the dependency tree. +native-tls = [ + "dep:hyper-tls", + "hyper-proxy2/tls", + "librespot-oauth/native-tls", + "tokio-tungstenite/native-tls", +] +rustls-tls-native-roots = [ + "__rustls", + "hyper-proxy2/rustls", + "hyper-rustls/native-tokio", + "librespot-oauth/rustls-tls-native-roots", + "tokio-tungstenite/rustls-tls-native-roots", +] +rustls-tls-webpki-roots = [ + "__rustls", + "hyper-proxy2/rustls-webpki", + "hyper-rustls/webpki-tokio", + "librespot-oauth/rustls-tls-webpki-roots", + "tokio-tungstenite/rustls-tls-webpki-roots", +] + +# Internal features - these are not meant to be used by end users +__rustls = [] + +[dependencies] +librespot-oauth = { version = "0.8.0", path = "../oauth", default-features = false } +librespot-protocol = { version = "0.8.0", path = "../protocol", default-features = false } + +aes = "0.8" +base64 = "0.22" +byteorder = "1.5" +bytes = "1" +data-encoding = "2.9" +flate2 = "1.1" +form_urlencoded = "1.2" +futures-core = "0.3" +futures-util = { version = "0.3", default-features = false, features = [ + "alloc", + "bilock", + "unstable", +] } +governor = { version = "0.10", default-features = false, features = ["std"] } +hmac = "0.12" +httparse = "1.10" +http = "1.3" +http-body-util = "0.1" +hyper = { version = "1.6", features = ["http1", "http2"] } +hyper-proxy2 = { version = "0.1", default-features = false } +hyper-rustls = { version = "0.27", default-features = false, features = [ + "http1", + "http2", + "ring", +], optional = true } +hyper-tls = { version = "0.6", optional = true } +hyper-util = { version = "0.1", default-features = false, features = [ + "client", + "http1", + "http2", +] } +log = "0.4" +nonzero_ext = "0.3" +num-bigint = "0.4" +num-derive = "0.4" +num-integer = "0.1" +num-traits = "0.2" +pbkdf2 = { version = "0.12", default-features = false, features = ["hmac"] } +pin-project-lite = "0.2" +priority-queue = "2.5" +protobuf = "3.7" +protobuf-json-mapping = "3.7" +quick-xml = { version = "0.38", features = ["serialize"] } +rand = { version = "0.9", default-features = false, features = ["thread_rng"] } +rsa = "0.9" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha1 = { version = "0.10", features = ["oid"] } +shannon = "0.2" +sysinfo = { version = "0.36", default-features = false, features = ["system"] } +thiserror = "2" +time = { version = "0.3", features = ["formatting", "parsing"] } +tokio = { version = "1", features = [ + "io-util", + "macros", + "net", + "rt", + "sync", + "time", +] } +tokio-stream = { version = "0.1", default-features = false } +tokio-tungstenite = { version = "0.28", default-features = false } +tokio-util = { version = "0.7", default-features = false } +url = "2" +uuid = { version = "1", default-features = false, features = ["v4"] } + +[build-dependencies] +rand = { version = "0.9", default-features = false, features = ["thread_rng"] } +rand_distr = "0.5" +vergen-gitcl = { version = "1.0", default-features = false, features = [ + "build", +] } + +[dev-dependencies] +tokio = { version = "1", features = ["macros"] } diff --git a/native/vendor/librespot-core/LICENSE b/native/vendor/librespot-core/LICENSE new file mode 100644 index 00000000..b06ecfb2 --- /dev/null +++ b/native/vendor/librespot-core/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paul Lietar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/native/vendor/librespot-core/build.rs b/native/vendor/librespot-core/build.rs new file mode 100644 index 00000000..4413d2f3 --- /dev/null +++ b/native/vendor/librespot-core/build.rs @@ -0,0 +1,31 @@ +use rand::Rng; +use rand_distr::Alphanumeric; +use vergen_gitcl::{BuildBuilder, Emitter, GitclBuilder}; + +fn main() -> Result<(), Box> { + let gitcl = GitclBuilder::default() + .sha(true) // outputs 'VERGEN_GIT_SHA', and sets the 'short' flag true + .commit_date(true) // outputs 'VERGEN_GIT_COMMIT_DATE' + .build()?; + + let build = BuildBuilder::default() + .build_date(true) // outputs 'VERGEN_BUILD_DATE' + .build()?; + + Emitter::default() + .add_instructions(&build)? + .add_instructions(&gitcl)? + .emit() + .expect("Unable to generate the cargo keys!"); + let build_id = match std::env::var("SOURCE_DATE_EPOCH") { + Ok(val) => val, + Err(_) => rand::rng() + .sample_iter(Alphanumeric) + .take(8) + .map(char::from) + .collect(), + }; + + println!("cargo:rustc-env=LIBRESPOT_BUILD_ID={build_id}"); + Ok(()) +} diff --git a/native/vendor/librespot-core/src/apresolve.rs b/native/vendor/librespot-core/src/apresolve.rs new file mode 100644 index 00000000..38dde797 --- /dev/null +++ b/native/vendor/librespot-core/src/apresolve.rs @@ -0,0 +1,153 @@ +use std::collections::VecDeque; + +use bytes::Bytes; +use hyper::{Method, Request}; +use serde::Deserialize; + +use crate::Error; + +pub type SocketAddress = (String, u16); + +#[derive(Default)] +pub struct AccessPoints { + accesspoint: VecDeque, + dealer: VecDeque, + spclient: VecDeque, +} + +#[derive(Deserialize, Default)] +pub struct ApResolveData { + accesspoint: Vec, + dealer: Vec, + spclient: Vec, +} + +impl ApResolveData { + // These addresses probably do some geo-location based traffic management or at least DNS-based + // load balancing. They are known to fail when the normal resolvers are up, so that's why they + // should only be used as fallback. + fn fallback() -> Self { + Self { + accesspoint: vec![String::from("ap.spotify.com:443")], + dealer: vec![String::from("dealer.spotify.com:443")], + spclient: vec![String::from("spclient.wg.spotify.com:443")], + } + } +} + +impl AccessPoints { + fn is_any_empty(&self) -> bool { + self.accesspoint.is_empty() || self.dealer.is_empty() || self.spclient.is_empty() + } +} + +component! { + ApResolver : ApResolverInner { + data: AccessPoints = AccessPoints::default(), + } +} + +impl ApResolver { + // return a port if a proxy URL and/or a proxy port was specified. This is useful even when + // there is no proxy, but firewalls only allow certain ports (e.g. 443 and not 4070). + pub fn port_config(&self) -> Option { + if self.session().config().proxy.is_some() || self.session().config().ap_port.is_some() { + Some(self.session().config().ap_port.unwrap_or(443)) + } else { + None + } + } + + fn process_ap_strings(&self, data: Vec) -> VecDeque { + let filter_port = self.port_config(); + data.into_iter() + .filter_map(|ap| { + let mut split = ap.rsplitn(2, ':'); + let port = split.next()?; + let port: u16 = port.parse().ok()?; + let host = split.next()?.to_owned(); + match filter_port { + Some(filter_port) if filter_port != port => None, + _ => Some((host, port)), + } + }) + .collect() + } + + fn parse_resolve_to_access_points(&self, resolve: ApResolveData) -> AccessPoints { + AccessPoints { + accesspoint: self.process_ap_strings(resolve.accesspoint), + dealer: self.process_ap_strings(resolve.dealer), + spclient: self.process_ap_strings(resolve.spclient), + } + } + + pub async fn try_apresolve(&self) -> Result { + let req = Request::builder() + .method(Method::GET) + .uri("https://apresolve.spotify.com/?type=accesspoint&type=dealer&type=spclient") + .body(Bytes::new())?; + + let body = self.session().http_client().request_body(req).await?; + let data: ApResolveData = serde_json::from_slice(body.as_ref())?; + + Ok(data) + } + + async fn apresolve(&self) { + let result = self.try_apresolve().await; + + self.lock(|inner| { + let (data, error) = match result { + Ok(data) => (data, None), + Err(e) => (ApResolveData::default(), Some(e)), + }; + + inner.data = self.parse_resolve_to_access_points(data); + + if inner.data.is_any_empty() { + warn!("Failed to resolve all access points, using fallbacks"); + if let Some(error) = error { + warn!("Resolve access points error: {error}"); + } + + let fallback = self.parse_resolve_to_access_points(ApResolveData::fallback()); + inner.data.accesspoint.extend(fallback.accesspoint); + inner.data.dealer.extend(fallback.dealer); + inner.data.spclient.extend(fallback.spclient); + } + }) + } + + fn is_any_empty(&self) -> bool { + self.lock(|inner| inner.data.is_any_empty()) + } + + pub async fn resolve(&self, endpoint: &str) -> Result { + if self.is_any_empty() { + self.apresolve().await; + } + + self.lock(|inner| { + let access_point = match endpoint { + // take the first position instead of the last with `pop`, because Spotify returns + // access points with ports 4070, 443 and 80 in order of preference from highest + // to lowest. + "accesspoint" => inner.data.accesspoint.pop_front(), + "dealer" => inner.data.dealer.pop_front(), + "spclient" => inner.data.spclient.pop_front(), + _ => { + return Err(Error::unimplemented(format!( + "No implementation to resolve access point {endpoint}" + ))); + } + }; + + let access_point = access_point.ok_or_else(|| { + Error::unavailable(format!("No access point available for endpoint {endpoint}")) + })?; + + Ok(access_point) + }) + } +} diff --git a/native/vendor/librespot-core/src/audio_key.rs b/native/vendor/librespot-core/src/audio_key.rs new file mode 100644 index 00000000..42cad43c --- /dev/null +++ b/native/vendor/librespot-core/src/audio_key.rs @@ -0,0 +1,110 @@ +use std::{collections::HashMap, io::Write, time::Duration}; + +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; +use bytes::Bytes; +use thiserror::Error; +use tokio::sync::oneshot; + +use crate::{Error, FileId, SpotifyId, packet::PacketType, util::SeqGenerator}; + +#[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] +pub struct AudioKey(pub [u8; 16]); + +#[derive(Debug, Error)] +pub enum AudioKeyError { + #[error("audio key error")] + AesKey, + #[error("other end of channel disconnected")] + Channel, + #[error("unexpected packet type {0}")] + Packet(u8), + #[error("sequence {0} not pending")] + Sequence(u32), + #[error("audio key response timeout")] + Timeout, +} + +impl From for Error { + fn from(err: AudioKeyError) -> Self { + match err { + AudioKeyError::AesKey => Error::unavailable(err), + AudioKeyError::Channel => Error::aborted(err), + AudioKeyError::Sequence(_) => Error::aborted(err), + AudioKeyError::Packet(_) => Error::unimplemented(err), + AudioKeyError::Timeout => Error::aborted(err), + } + } +} + +component! { + AudioKeyManager : AudioKeyManagerInner { + sequence: SeqGenerator = SeqGenerator::new(0), + pending: HashMap>> = HashMap::new(), + } +} + +impl AudioKeyManager { + pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) -> Result<(), Error> { + let seq = BigEndian::read_u32(data.split_to(4).as_ref()); + + let sender = self + .lock(|inner| inner.pending.remove(&seq)) + .ok_or(AudioKeyError::Sequence(seq))?; + + match cmd { + PacketType::AesKey => { + let mut key = [0u8; 16]; + key.copy_from_slice(data.as_ref()); + sender + .send(Ok(AudioKey(key))) + .map_err(|_| AudioKeyError::Channel)? + } + PacketType::AesKeyError => { + error!( + "error audio key {:x} {:x}", + data.as_ref()[0], + data.as_ref()[1] + ); + sender + .send(Err(AudioKeyError::AesKey.into())) + .map_err(|_| AudioKeyError::Channel)? + } + _ => { + trace!("Did not expect {cmd:?} AES key packet with data {data:#?}"); + return Err(AudioKeyError::Packet(cmd as u8).into()); + } + } + + Ok(()) + } + + pub async fn request(&self, track: SpotifyId, file: FileId) -> Result { + let (tx, rx) = oneshot::channel(); + + let seq = self.lock(move |inner| { + let seq = inner.sequence.get(); + inner.pending.insert(seq, tx); + seq + }); + + self.send_key_request(seq, track, file)?; + const KEY_RESPONSE_TIMEOUT: Duration = Duration::from_millis(1500); + match tokio::time::timeout(KEY_RESPONSE_TIMEOUT, rx).await { + Err(_) => { + error!("Audio key response timeout"); + Err(AudioKeyError::Timeout.into()) + } + Ok(k) => k?, + } + } + + fn send_key_request(&self, seq: u32, track: SpotifyId, file: FileId) -> Result<(), Error> { + let mut data: Vec = Vec::new(); + data.write_all(&file.0)?; + data.write_all(&track.to_raw())?; + data.write_u32::(seq)?; + data.write_u16::(0x0000)?; + + self.session().send_packet(PacketType::RequestKey, data) + } +} diff --git a/native/vendor/librespot-core/src/authentication.rs b/native/vendor/librespot-core/src/authentication.rs new file mode 100644 index 00000000..62bab6a8 --- /dev/null +++ b/native/vendor/librespot-core/src/authentication.rs @@ -0,0 +1,189 @@ +use std::io::{self, Read}; + +use aes::Aes192; +use base64::engine::Engine as _; +use base64::engine::general_purpose::STANDARD as BASE64; +use byteorder::{BigEndian, ByteOrder}; +use pbkdf2::pbkdf2_hmac; +use protobuf::Enum; +use serde::{Deserialize, Serialize}; +use sha1::{Digest, Sha1}; +use thiserror::Error; + +use crate::{Error, protocol::authentication::AuthenticationType}; + +#[derive(Debug, Error)] +pub enum AuthenticationError { + #[error("unknown authentication type {0}")] + AuthType(u32), + #[error("invalid key")] + Key, +} + +impl From for Error { + fn from(err: AuthenticationError) -> Self { + Error::invalid_argument(err) + } +} + +/// The credentials are used to log into the Spotify API. +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +pub struct Credentials { + pub username: Option, + + #[serde(serialize_with = "serialize_protobuf_enum")] + #[serde(deserialize_with = "deserialize_protobuf_enum")] + pub auth_type: AuthenticationType, + + #[serde(alias = "encoded_auth_blob")] + #[serde(serialize_with = "serialize_base64")] + #[serde(deserialize_with = "deserialize_base64")] + pub auth_data: Vec, +} + +impl Credentials { + /// Intialize these credentials from a username and a password. + /// + /// ### Example + /// ```rust + /// use librespot_core::authentication::Credentials; + /// + /// let creds = Credentials::with_password("my account", "my password"); + /// ``` + pub fn with_password(username: impl Into, password: impl Into) -> Self { + Self { + username: Some(username.into()), + auth_type: AuthenticationType::AUTHENTICATION_USER_PASS, + auth_data: password.into().into_bytes(), + } + } + + pub fn with_access_token(token: impl Into) -> Self { + Self { + username: None, + auth_type: AuthenticationType::AUTHENTICATION_SPOTIFY_TOKEN, + auth_data: token.into().into_bytes(), + } + } + + #[expect(deprecated)] + pub fn with_blob( + username: impl Into, + encrypted_blob: impl AsRef<[u8]>, + device_id: impl AsRef<[u8]>, + ) -> Result { + fn read_u8(stream: &mut R) -> io::Result { + let mut data = [0u8]; + stream.read_exact(&mut data)?; + Ok(data[0]) + } + + fn read_int(stream: &mut R) -> io::Result { + let lo = read_u8(stream)? as u32; + if lo & 0x80 == 0 { + return Ok(lo); + } + + let hi = read_u8(stream)? as u32; + Ok(lo & 0x7f | (hi << 7)) + } + + fn read_bytes(stream: &mut R) -> io::Result> { + let length = read_int(stream)?; + let mut data = vec![0u8; length as usize]; + stream.read_exact(&mut data)?; + + Ok(data) + } + + let username = username.into(); + + let secret = Sha1::digest(device_id.as_ref()); + + let key = { + let mut key = [0u8; 24]; + if key.len() < 20 { + return Err(AuthenticationError::Key.into()); + } + + pbkdf2_hmac::(&secret, username.as_bytes(), 0x100, &mut key[0..20]); + + let hash = &Sha1::digest(&key[..20]); + key[..20].copy_from_slice(hash); + BigEndian::write_u32(&mut key[20..], 20); + key + }; + + // decrypt data using ECB mode without padding + let blob = { + use aes::cipher::generic_array::GenericArray; + use aes::cipher::{BlockDecrypt, BlockSizeUser, KeyInit}; + + let mut data = BASE64.decode(encrypted_blob)?; + let cipher = Aes192::new(GenericArray::from_slice(&key)); + let block_size = Aes192::block_size(); + + for chunk in data.chunks_exact_mut(block_size) { + cipher.decrypt_block(GenericArray::from_mut_slice(chunk)); + } + + let l = data.len(); + for i in 0..l - 0x10 { + data[l - i - 1] ^= data[l - i - 0x11]; + } + + data + }; + + let mut cursor = io::Cursor::new(blob.as_slice()); + read_u8(&mut cursor)?; + read_bytes(&mut cursor)?; + read_u8(&mut cursor)?; + let auth_type = read_int(&mut cursor)?; + let auth_type = AuthenticationType::from_i32(auth_type as i32) + .ok_or(AuthenticationError::AuthType(auth_type))?; + read_u8(&mut cursor)?; + let auth_data = read_bytes(&mut cursor)?; + + Ok(Self { + username: Some(username), + auth_type, + auth_data, + }) + } +} + +fn serialize_protobuf_enum(v: &T, ser: S) -> Result +where + T: Enum, + S: serde::Serializer, +{ + serde::Serialize::serialize(&v.value(), ser) +} + +fn deserialize_protobuf_enum<'de, T, D>(de: D) -> Result +where + T: Enum, + D: serde::Deserializer<'de>, +{ + let v: i32 = serde::Deserialize::deserialize(de)?; + T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value")) +} + +fn serialize_base64(v: &T, ser: S) -> Result +where + T: AsRef<[u8]>, + S: serde::Serializer, +{ + serde::Serialize::serialize(&BASE64.encode(v.as_ref()), ser) +} + +fn deserialize_base64<'de, D>(de: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + let v: String = serde::Deserialize::deserialize(de)?; + BASE64 + .decode(v) + .map_err(|e| serde::de::Error::custom(e.to_string())) +} diff --git a/native/vendor/librespot-core/src/cache.rs b/native/vendor/librespot-core/src/cache.rs new file mode 100644 index 00000000..15e35d21 --- /dev/null +++ b/native/vendor/librespot-core/src/cache.rs @@ -0,0 +1,494 @@ +use std::{ + cmp::Reverse, + collections::HashMap, + fs::{self, File}, + io::{self, Read, Write}, + path::{Path, PathBuf}, + sync::{Arc, Mutex}, + time::SystemTime, +}; + +use priority_queue::PriorityQueue; +use thiserror::Error; + +use crate::{Error, FileId, authentication::Credentials, error::ErrorKind}; + +const CACHE_LIMITER_POISON_MSG: &str = "cache limiter mutex should not be poisoned"; + +#[derive(Debug, Error)] +pub enum CacheError { + #[error("audio cache location is not configured")] + Path, +} + +impl From for Error { + fn from(err: CacheError) -> Self { + Error::failed_precondition(err) + } +} + +/// Some kind of data structure that holds some paths, the size of these files and a timestamp. +/// It keeps track of the file sizes and is able to pop the path with the oldest timestamp if +/// a given limit is exceeded. +struct SizeLimiter { + queue: PriorityQueue>, + sizes: HashMap, + size_limit: u64, + in_use: u64, +} + +impl SizeLimiter { + /// Creates a new instance with the given size limit. + fn new(limit: u64) -> Self { + Self { + queue: PriorityQueue::new(), + sizes: HashMap::new(), + size_limit: limit, + in_use: 0, + } + } + + /// Adds an entry to this data structure. + /// + /// If this file is already contained, it will be updated accordingly. + fn add(&mut self, file: &Path, size: u64, accessed: SystemTime) { + self.in_use += size; + self.queue.push(file.to_owned(), Reverse(accessed)); + if let Some(old_size) = self.sizes.insert(file.to_owned(), size) { + // It's important that decreasing happens after + // increasing the size, to prevent an overflow. + self.in_use -= old_size; + } + } + + /// Returns true if the limit is exceeded. + fn exceeds_limit(&self) -> bool { + self.in_use > self.size_limit + } + + /// Returns the least recently accessed file if the size of the cache exceeds + /// the limit. + /// + /// The entry is removed from the data structure, but the caller is responsible + /// to delete the file in the file system. + fn pop(&mut self) -> Option { + if self.exceeds_limit() { + if let Some((next, _)) = self.queue.pop() { + if let Some(size) = self.sizes.remove(&next) { + self.in_use -= size; + } else { + error!("`queue` and `sizes` should have the same keys."); + } + Some(next) + } else { + error!("in_use was > 0, so the queue should have contained an item."); + None + } + } else { + None + } + } + + /// Updates the timestamp of an existing element. Returns `true` if the item did exist. + fn update(&mut self, file: &Path, access_time: SystemTime) -> bool { + self.queue + .change_priority(file, Reverse(access_time)) + .is_some() + } + + /// Removes an element with the specified path. Returns `true` if the item did exist. + fn remove(&mut self, file: &Path) -> bool { + if self.queue.remove(file).is_none() { + return false; + } + + if let Some(size) = self.sizes.remove(file) { + self.in_use -= size; + } else { + error!("`queue` and `sizes` should have the same keys."); + } + + true + } +} + +struct FsSizeLimiter { + limiter: Mutex, +} + +impl FsSizeLimiter { + /// Returns access time and file size of a given path. + fn get_metadata(file: &Path) -> io::Result<(SystemTime, u64)> { + let metadata = file.metadata()?; + + // The first of the following timestamps which is available will be chosen as access time: + // 1. Access time + // 2. Modification time + // 3. Creation time + // 4. Current time + let access_time = metadata + .accessed() + .or_else(|_| metadata.modified()) + .or_else(|_| metadata.created()) + .unwrap_or_else(|_| SystemTime::now()); + + let size = metadata.len(); + + Ok((access_time, size)) + } + + /// Recursively search a directory for files and add them to the `limiter` struct. + fn init_dir(limiter: &mut SizeLimiter, path: &Path) { + let list_dir = match fs::read_dir(path) { + Ok(list_dir) => list_dir, + Err(e) => { + warn!("Could not read directory {path:?} in cache dir: {e}"); + return; + } + }; + + for entry in list_dir { + let entry = match entry { + Ok(entry) => entry, + Err(e) => { + warn!("Could not directory {path:?} in cache dir: {e}"); + return; + } + }; + + match entry.file_type() { + Ok(file_type) if file_type.is_dir() || file_type.is_symlink() => { + Self::init_dir(limiter, &entry.path()) + } + Ok(file_type) if file_type.is_file() => { + let path = entry.path(); + match Self::get_metadata(&path) { + Ok((access_time, size)) => { + limiter.add(&path, size, access_time); + } + Err(e) => { + warn!("Could not read file {path:?} in cache dir: {e}") + } + } + } + Ok(ft) => { + warn!( + "File {:?} in cache dir has unsupported type {:?}", + entry.path(), + ft + ) + } + Err(e) => { + warn!( + "Could not get type of file {:?} in cache dir: {}", + entry.path(), + e + ) + } + }; + } + } + + fn add(&self, file: &Path, size: u64) { + self.limiter + .lock() + .expect(CACHE_LIMITER_POISON_MSG) + .add(file, size, SystemTime::now()) + } + + fn touch(&self, file: &Path) -> bool { + self.limiter + .lock() + .expect(CACHE_LIMITER_POISON_MSG) + .update(file, SystemTime::now()) + } + + fn remove(&self, file: &Path) -> bool { + self.limiter + .lock() + .expect(CACHE_LIMITER_POISON_MSG) + .remove(file) + } + + fn prune_internal Option>(mut pop: F) -> Result<(), Error> { + let mut first = true; + let mut count = 0; + let mut last_error = None; + + while let Some(file) = pop() { + if first { + debug!("Cache dir exceeds limit, removing least recently used files."); + first = false; + } + + let res = fs::remove_file(&file); + if let Err(e) = res { + warn!("Could not remove file {file:?} from cache dir: {e}"); + last_error = Some(e); + } else { + count += 1; + } + } + + if count > 0 { + info!("Removed {count} cache files."); + } + + if let Some(err) = last_error { + Err(err.into()) + } else { + Ok(()) + } + } + + fn prune(&self) -> Result<(), Error> { + Self::prune_internal(|| self.limiter.lock().expect(CACHE_LIMITER_POISON_MSG).pop()) + } + + fn new(path: &Path, limit: u64) -> Result { + let mut limiter = SizeLimiter::new(limit); + + Self::init_dir(&mut limiter, path); + Self::prune_internal(|| limiter.pop())?; + + Ok(Self { + limiter: Mutex::new(limiter), + }) + } +} + +/// A cache for volume, credentials and audio files. +#[derive(Clone)] +pub struct Cache { + credentials_location: Option, + volume_location: Option, + audio_location: Option, + size_limiter: Option>, +} + +impl Cache { + pub fn new>( + credentials_path: Option

, + volume_path: Option

, + audio_path: Option

@2(yE{=Vf{>X%b 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 iKx2XVAbC*a41bwa@{t?0*cAx;Q{Qm&gQ=&is literal 34654 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
@C6T2~X>Ln0vd#fmumF->98 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 RUV(rg83}pua#6#;{{@~(k~9DS diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 53fd9dfb..ac734375 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -228,6 +228,11 @@ android:name=".apps.music.MusicService" android:exported="false" android:foregroundServiceType="mediaPlayback" /> + + diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index d9d34749..a5412701 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -55,6 +55,7 @@ import hrln.interactive.sidekit.apps.media.formatDuration import hrln.interactive.sidekit.design.action.ActionMotion import hrln.interactive.sidekit.design.action.ActionTone import hrln.interactive.sidekit.design.action.SideAction +import hrln.interactive.sidekit.design.action.undoBar import hrln.interactive.sidekit.design.components.SidekitIconImage import hrln.interactive.sidekit.design.components.SidekitIconItem import hrln.interactive.sidekit.design.components.SidekitItem @@ -286,6 +287,25 @@ fun withNotice(notice: String?, onDismiss: () -> Unit, actions: List fun spotifyBack(label: String, onBack: () -> Unit) = listOf(SideAction.back("back", label, onBack)) +/** + * The action bar for screens that can change saved state. While an Undo offer is live it takes the + * right slot; otherwise the bar shows the screen's normal actions and any notice. + */ +fun spotifyBar( + state: SpotifyUiState, + onDismissNotice: () -> Unit, + onUndo: () -> Unit, + continueWith: SideAction, + extra: List = emptyList() +): List { + val undo = state.undo + return if (undo != null) { + undoBar(continueWith, SideAction.undo(contentDescription = undo.contentDescription, onClick = onUndo)) + } else { + withNotice(state.notice, onDismissNotice, listOf(continueWith) + extra) + } +} + fun repeatLabel(repeat: RepeatMode) = repeat.label /** @@ -397,24 +417,3 @@ fun SpotifyLibraryRow( } } } - -/** Compact "Up next" affordance; a full Item would steal height from the artwork. */ -@Composable -fun UpNextRow(title: String, modifier: Modifier = Modifier, onClick: () -> Unit) { - val colors = SidekitTheme.colors - Row( - modifier - .fillMaxWidth() - .height(46.dp) - .clip(SidekitTheme.shapes.control) - .background(colors.surfaceA3) - .clickable(onClickLabel = "Open queue", onClick = onClick) - .padding(horizontal = 18.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(10.dp) - ) { - SidekitText("Up next", style = SidekitTheme.typography.caption, color = colors.surfaceA6) - SidekitText(title, Modifier.weight(1f), style = SidekitTheme.typography.caption, maxLines = 1) - SidekitIconImage(SidekitIcon.ArrowRightAlt, size = 26.dp) - } -} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 74011220..38150a86 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -24,6 +24,7 @@ import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions import hrln.interactive.sidekit.design.components.SidekitScaffold import hrln.interactive.sidekit.navigation.LAUNCHER_ROUTE import hrln.interactive.sidekit.navigation.SubApp +import hrln.interactive.sidekit.ui.UndoWindowLifecycle private val ROOT = SubApp.Sidetracks.route private val HOME = "$ROOT/home" @@ -62,6 +63,8 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onSettings = { nav.navigate(SETTINGS) }, onRefresh = vm::refresh, onPlay = vm::playLibraryTrack, + onQueue = { nav.navigate(QUEUE) }, + onQueueTrack = vm::addToQueue, onDismissNotice = vm::dismissNotice ) } @@ -73,7 +76,11 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onBack = { nav.popBackStack() }, onSearch = { nav.navigate(SEARCH) }, onFilter = vm::setFilter, - onTrack = vm::playItem, + onTrack = { + vm.playItem(it) + nav.navigate(NOW_PLAYING) + }, + onQueue = vm::addToQueue, onPlaylist = { nav.navigate(collectionRoute(SpotifyCollectionType.Playlist, it.id)) }, onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onArtist = { nav.navigate(artistRoute(it.id)) }, @@ -84,6 +91,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(COLLECTION, arguments = collectionArgs) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() + UndoWindowLifecycle(vm.undoWindow, vm::commitUndo) val type = SpotifyCollectionType.entries.firstOrNull { it.route == entry.arguments?.getString("type") } val id = entry.arguments?.getString("id").orEmpty() LaunchedEffect(type, id) { type?.let { vm.ensureCollection(it, id) } } @@ -102,7 +110,9 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { collection.artistId?.let { nav.navigate(artistRoute(it)) } ?: vm.notice("This collection has no artist page") }, onQueue = { collection.items.firstOrNull()?.let(vm::addToQueue) }, + onQueueItem = vm::addToQueue, onNowPlaying = { nav.navigate(NOW_PLAYING) }, + onUndo = vm::undo, onDismissNotice = vm::dismissNotice ) } @@ -110,6 +120,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(ARTIST, arguments = artistArgs) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() + UndoWindowLifecycle(vm.undoWindow, vm::commitUndo) val artistId = entry.arguments?.getString("id").orEmpty() LaunchedEffect(artistId) { vm.ensureArtist(artistId) } val artist = vm.artist(artistId) @@ -126,9 +137,11 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onBack = { nav.popBackStack() }, onPlay = { vm.playArtist(artist) }, onTrack = { vm.play(tracks, tracks.indexOf(it)) }, + onQueue = vm::addToQueue, onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onFollow = { vm.toggleFollow(artist) }, onNowPlaying = { nav.navigate(NOW_PLAYING) }, + onUndo = vm::undo, onDismissNotice = vm::dismissNotice ) } @@ -143,6 +156,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onClear = vm::clearQuery, onToggleScope = vm::toggleScope, onTrack = vm::playItem, + onQueue = vm::addToQueue, onArtist = { nav.navigate(artistRoute(it.id)) }, onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onPlaylist = { nav.navigate(collectionRoute(SpotifyCollectionType.Playlist, it.id)) }, @@ -153,6 +167,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { composable(NOW_PLAYING) { entry -> val vm = entry.spotifyViewModel(nav) val state by vm.state.collectAsStateWithLifecycle() + UndoWindowLifecycle(vm.undoWindow, vm::commitUndo) SpotifyNowPlayingScreen( state, onBack = { nav.popBackStack() }, @@ -172,6 +187,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onArtist = { state.current?.artistId?.let { nav.navigate(artistRoute(it)) } }, + onUndo = vm::undo, onDismissNotice = vm::dismissNotice ) } @@ -181,7 +197,11 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { QueueScreen( state, onBack = { nav.popBackStack() }, - onNowPlaying = { nav.popBackStack(NOW_PLAYING, inclusive = false) }, + onNowPlaying = { + if (!nav.popBackStack(NOW_PLAYING, inclusive = false)) { + nav.navigate(NOW_PLAYING) { launchSingleTop = true } + } + }, onJump = vm::jumpTo, onPlayNext = vm::moveToNext, onRemove = vm::removeUpcoming, @@ -198,7 +218,6 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { state, onBack = { nav.popBackStack() }, onAudioQuality = vm::cycleAudioQuality, - onVolume = vm::setVolume, onClearCache = vm::clearCache, onClearHistory = vm::clearHistory, onSignOut = { @@ -264,6 +283,9 @@ private fun NavBackStackEntry.spotifyViewModel(nav: NavHostController): SpotifyV initializer { SpotifyViewModel( catalog = session.catalog, + writer = session.writer, + queue = session.queue, + history = session.history, session = session, openBrowser = openBrowser, clearArtworkCache = { RemoteArtwork.clear(context) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 1eef5147..678d4fce 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -1,11 +1,8 @@ package hrln.interactive.sidekit.apps.spotify -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState @@ -24,8 +21,11 @@ import hrln.interactive.sidekit.design.components.SidekitItem import hrln.interactive.sidekit.design.components.SidekitItemRow import hrln.interactive.sidekit.design.components.SidekitList import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitSwipeRow import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.SwipeAction import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.components.rememberSwipeRevealGroup import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme @@ -38,6 +38,8 @@ fun SidetracksHomeScreen( onSettings: () -> Unit, onRefresh: () -> Unit, onPlay: (SpotifyTrack) -> Unit, + onQueue: () -> Unit, + onQueueTrack: (SpotifyTrack) -> Unit, onDismissNotice: () -> Unit ) { val recent = state.recent.take(10) @@ -45,20 +47,7 @@ fun SidetracksHomeScreen( state.notice, onDismissNotice, listOf( - SideAction( - "nowPlaying", - square = true, - contentDescription = "Now Playing", - artKey = state.current?.id, - art = { - if (state.current != null) { - SpotifyCover(state.current.artRes, Modifier.height(SidekitTheme.spacing.tileIconSize), artUrl = state.current.artUrl) - } else { - hrln.interactive.sidekit.design.components.SidekitIconImage(SidekitIcon.MusicNote2) - } - }, - onClick = onNowPlaying - ), + SideAction("queue", "Queue", SidekitIcon.QueueMusic, onClick = onQueue), SideAction("search", "Search", SidekitIcon.Search, motion = ActionMotion.Forward, onClick = onSearch) ) ) @@ -71,6 +60,7 @@ fun SidetracksHomeScreen( ) ) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() SidekitList(padding, state = listState) { item(key = "tiles") { SidekitItemRow(Modifier.listFade(listState, 0)) { @@ -83,18 +73,34 @@ fun SidetracksHomeScreen( item(key = "empty") { EmptyMessage("Nothing played yet", "Search to find your first track") } } else { itemsIndexed(recent, key = { _, track -> track.id }) { index, track -> - SpotifyMediaRow( - track, - Modifier.listFade(listState, index + 2), - secondary = track.artist, - onClick = { onPlay(track) } - ) + SidekitSwipeRow( + key = track.id, + group = swipes, + modifier = Modifier.listFade(listState, index + 2), + action = queueAction(track, onQueueTrack) + ) { + SpotifyMediaRow( + track, + Modifier, + secondary = track.artist, + onClick = { onPlay(track) } + ) + } } } } } } +@Composable +private fun queueAction(item: T, onQueue: (T) -> Unit) = SwipeAction( + label = "Add ${item.title} to queue", + icon = SidekitIcon.QueueMusic, + color = SidekitTheme.colors.primaryA1, + armedColor = SidekitTheme.colors.primaryA2, + onClick = { onQueue(item) } +) + @Composable fun LibraryScreen( state: SpotifyUiState, @@ -102,13 +108,13 @@ fun LibraryScreen( onSearch: () -> Unit, onFilter: (LibraryFilter) -> Unit, onTrack: (SpotifyTrack) -> Unit, + onQueue: (SpotifyTrack) -> Unit, onPlaylist: (SpotifyPlaylist) -> Unit, onAlbum: (SpotifyAlbum) -> Unit, onArtist: (SpotifyArtist) -> Unit, onShow: (SpotifyShow) -> Unit, onDismissNotice: () -> Unit ) { - val colors = SidekitTheme.colors val actions = withNotice( state.notice, onDismissNotice, @@ -123,21 +129,12 @@ fun LibraryScreen( ) } SidekitScaffold( - title = null, - header = { - Column( - Modifier.fillMaxWidth().height(SidekitTheme.spacing.headerHeight) - .padding(horizontal = SidekitTheme.spacing.itemPadding).padding(top = 14.dp), - verticalArrangement = Arrangement.spacedBy(0.dp) - ) { - SidekitText("Library", style = SidekitTheme.typography.headline) - SidekitText(state.filter.label, style = SidekitTheme.typography.caption, color = colors.surfaceA6) - } - }, + title = "Library", actions = actions, options = filters ) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() val first = remember { FocusRequester() } LaunchedEffect(state.filter) { runCatching { first.requestFocus() } } SidekitList(padding, state = listState) { @@ -148,11 +145,23 @@ fun LibraryScreen( itemsIndexed(items, key = { index, item -> "$index:${libraryKey(item)}" }) { index, item -> val modifier = Modifier.listFade(listState, index).then(if (index == 0) Modifier.focusRequester(first) else Modifier) when (item) { - is SpotifyTrack -> SpotifyMediaRow(item, modifier, secondary = item.artist, onClick = { onTrack(item) }) + is SpotifyTrack -> SidekitSwipeRow( + key = item.id, + group = swipes, + modifier = modifier, + action = queueAction(item, onQueue) + ) { + SpotifyMediaRow(item, Modifier, secondary = item.artist, onClick = { onTrack(item) }) + } + is SpotifyPlaylist -> SpotifyLibraryRow(item.artRes, item.name, "${item.owner} · ${item.trackCount} tracks", modifier, artUrl = item.artUrl) { onPlaylist(item) } + is SpotifyAlbum -> SpotifyLibraryRow(item.artRes, item.title, "${item.artist} · ${item.year}", modifier, artUrl = item.artUrl) { onAlbum(item) } + is SpotifyArtist -> SpotifyLibraryRow(item.artRes, item.name, "Artist", modifier, artUrl = item.artUrl) { onArtist(item) } + is SpotifyShow -> SpotifyLibraryRow(item.artRes, item.name, item.publisher, modifier, artUrl = item.artUrl) { onShow(item) } + else -> Unit } } @@ -170,12 +179,14 @@ fun CollectionScreen( onSave: () -> Unit, onArtist: () -> Unit, onQueue: () -> Unit, + onQueueItem: (SpotifyMediaItem) -> Unit, onNowPlaying: () -> Unit, + onUndo: () -> Unit, onDismissNotice: () -> Unit ) { val colors = SidekitTheme.colors val saved = "${collection.type.route}:${collection.id}" in state.savedCollectionKeys - val actions = withNotice(state.notice, onDismissNotice, spotifyBack("Back", onBack)) + val actions = spotifyBar(state, onDismissNotice, onUndo, spotifyBack("Back", onBack).first()) val options = buildList { add( SideAction( @@ -192,6 +203,7 @@ fun CollectionScreen( } SidekitScaffold(title = collection.title, actions = actions, options = options) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() val playFocus = remember { FocusRequester() } LaunchedEffect(collection.type, collection.id) { runCatching { playFocus.requestFocus() } } SidekitList(padding, state = listState) { @@ -220,14 +232,21 @@ fun CollectionScreen( ) } itemsIndexed(collection.items, key = { _, item -> item.id }) { index, item -> - SpotifyMediaRow( - item, - Modifier.listFade(listState, index + 2), - index = index, - showArt = false, - secondary = if (item is SpotifyEpisode) formatPublishedDate(item.published) else item.creator, - onClick = { onItem(index) } - ) + SidekitSwipeRow( + key = item.id, + group = swipes, + modifier = Modifier.listFade(listState, index + 2), + action = queueAction(item, onQueueItem) + ) { + SpotifyMediaRow( + item, + Modifier, + index = index, + showArt = false, + secondary = if (item is SpotifyEpisode) formatPublishedDate(item.published) else item.creator, + onClick = { onItem(index) } + ) + } } } } @@ -242,13 +261,15 @@ fun ArtistScreen( onBack: () -> Unit, onPlay: () -> Unit, onTrack: (SpotifyTrack) -> Unit, + onQueue: (SpotifyTrack) -> Unit, onAlbum: (SpotifyAlbum) -> Unit, onFollow: () -> Unit, onNowPlaying: () -> Unit, + onUndo: () -> Unit, onDismissNotice: () -> Unit ) { val followed = artist.id in state.followedArtistIds - val actions = withNotice(state.notice, onDismissNotice, spotifyBack("Back", onBack)) + val actions = spotifyBar(state, onDismissNotice, onUndo, spotifyBack("Back", onBack).first()) SidekitScaffold( title = artist.name, actions = actions, @@ -258,6 +279,7 @@ fun ArtistScreen( ) ) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() SidekitList(padding, state = listState) { item(key = "tiles") { SidekitItemRow(Modifier.listFade(listState, 0)) { @@ -267,7 +289,14 @@ fun ArtistScreen( } item(key = "topLabel") { SectionLabel("Top tracks") } itemsIndexed(tracks.take(10), key = { _, track -> track.id }) { index, track -> - SpotifyMediaRow(track, Modifier.listFade(listState, index + 2), secondary = track.artist, onClick = { onTrack(track) }) + SidekitSwipeRow( + key = track.id, + group = swipes, + modifier = Modifier.listFade(listState, index + 2), + action = queueAction(track, onQueue) + ) { + SpotifyMediaRow(track, Modifier, secondary = track.artist, onClick = { onTrack(track) }) + } } item(key = "albumsLabel") { SectionLabel("Albums") } itemsIndexed(albums, key = { _, album -> album.id }) { index, album -> diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt index 8d944982..c47b1379 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt @@ -157,10 +157,10 @@ enum class RepeatMode(val label: String) { } } -enum class AudioQuality(val label: String) { - Normal("Normal"), - High("High"), - VeryHigh("Very high") +enum class AudioQuality(val label: String, val bitrateKbps: Int) { + Normal("Normal", 96), + High("High", 160), + VeryHigh("Very high", 320) ; fun next(): AudioQuality = entries[(ordinal + 1) % entries.size] diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 5b7a41ba..3e448dc2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -54,27 +54,26 @@ fun SpotifyNowPlayingScreen( onQueue: () -> Unit, onAlbum: () -> Unit, onArtist: () -> Unit, + onUndo: () -> Unit, onDismissNotice: () -> Unit ) { val current = state.current - val actions = withNotice( - state.notice, + val save = current?.let { + SideAction.square( + "save", + if (state.savedCurrent) SidekitIcon.Bookmark else SidekitIcon.BookmarkBorder, + if (state.savedCurrent) "Saved" else "Save", + onSave, + tone = if (state.savedCurrent) ActionTone.Secondary else ActionTone.Muted, + motion = ActionMotion.Up + ) + } + val actions = spotifyBar( + state, onDismissNotice, - buildList { - add(spotifyBack("Back", onBack).first()) - if (current != null) { - add( - SideAction.square( - "save", - if (state.savedCurrent) SidekitIcon.Check else SidekitIcon.Bookmark, - if (state.savedCurrent) "Saved" else "Save", - onSave, - tone = if (state.savedCurrent) ActionTone.Secondary else ActionTone.Muted, - motion = ActionMotion.Up - ) - ) - } - } + onUndo, + spotifyBack("Back", onBack).first(), + listOfNotNull(save) ) val options = if (current == null) { emptyList() @@ -82,7 +81,6 @@ fun SpotifyNowPlayingScreen( buildList { add(SideAction("queue", "Queue", SidekitIcon.QueueMusic, onClick = onQueue)) if (current is SpotifyTrack) add(SideAction("album", "Album", SidekitIcon.Album, onClick = onAlbum)) - if (current.artistId != null) add(SideAction("artist", "Artist", SidekitIcon.Artist, onClick = onArtist)) } } SidekitScaffold(title = null, actions = actions, options = options) { padding -> @@ -152,7 +150,6 @@ fun SpotifyNowPlayingScreen( 0.85f ) } - state.upcoming.firstOrNull()?.let { UpNextRow(it.title, onClick = onQueue) } } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 1b423d1f..0b194d98 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -21,8 +21,11 @@ import hrln.interactive.sidekit.design.action.ActionTone import hrln.interactive.sidekit.design.action.SideAction import hrln.interactive.sidekit.design.components.SidekitList import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitSwipeRow import hrln.interactive.sidekit.design.components.SidekitTextField +import hrln.interactive.sidekit.design.components.SwipeAction import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.components.rememberSwipeRevealGroup import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme @@ -34,6 +37,7 @@ fun SpotifySearchScreen( onClear: () -> Unit, onToggleScope: (SearchScope) -> Unit, onTrack: (SpotifyTrack) -> Unit, + onQueue: (SpotifyTrack) -> Unit, onArtist: (SpotifyArtist) -> Unit, onAlbum: (SpotifyAlbum) -> Unit, onPlaylist: (SpotifyPlaylist) -> Unit, @@ -83,6 +87,7 @@ fun SpotifySearchScreen( options = scopes ) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() SidekitList(padding, state = listState) { when { state.query.isBlank() -> item(key = "idle") { @@ -107,7 +112,20 @@ fun SpotifySearchScreen( item(key = "tracksLabel") { SectionLabel("Tracks") } val shown = if (expandedTracks) results.tracks else results.tracks.take(5) itemsIndexed(shown, key = { _, track -> "t${track.id}" }) { index, track -> - SpotifyMediaRow(track, Modifier.listFade(listState, slot + index), secondary = track.artist) { onTrack(track) } + SidekitSwipeRow( + key = track.id, + group = swipes, + modifier = Modifier.listFade(listState, slot + index), + action = SwipeAction( + label = "Add ${track.title} to queue", + icon = SidekitIcon.QueueMusic, + color = SidekitTheme.colors.primaryA1, + armedColor = SidekitTheme.colors.primaryA2, + onClick = { onQueue(track) } + ) + ) { + SpotifyMediaRow(track, Modifier, secondary = track.artist) { onTrack(track) } + } } slot += shown.size if (!expandedTracks && results.tracks.size > 5) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index b5c65ce8..6573bd33 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -23,12 +23,10 @@ import hrln.interactive.sidekit.design.components.SidekitCard import hrln.interactive.sidekit.design.components.SidekitItem import hrln.interactive.sidekit.design.components.SidekitList import hrln.interactive.sidekit.design.components.SidekitScaffold -import hrln.interactive.sidekit.design.components.SidekitSliderItem import hrln.interactive.sidekit.design.components.SidekitText import hrln.interactive.sidekit.design.components.listFade import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme -import kotlin.math.roundToInt private enum class SettingsConfirmation { SignOut, ClearHistory } @@ -37,7 +35,6 @@ fun SpotifySettingsScreen( state: SpotifyUiState, onBack: () -> Unit, onAudioQuality: () -> Unit, - onVolume: (Float) -> Unit, onClearCache: () -> Unit, onClearHistory: () -> Unit, onSignOut: () -> Unit, @@ -97,18 +94,9 @@ fun SpotifySettingsScreen( onClick = onAudioQuality ) } - item(key = "volume") { - SidekitSliderItem( - SidekitIcon.MusicNote2, - "Volume ${(state.volume * 100).roundToInt()}%", - state.volume, - onVolume, - Modifier.listFade(listState, 1) - ) - } item(key = "streaming") { SidekitCard( - Modifier.listFade(listState, 2).fillMaxWidth(), + Modifier.listFade(listState, 1).fillMaxWidth(), color = SidekitTheme.colors.surfaceA2 ) { Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { @@ -125,7 +113,7 @@ fun SpotifySettingsScreen( item(key = "cache") { SidekitItem( "Artwork cache", - Modifier.listFade(listState, 3), + Modifier.listFade(listState, 2), secondary = "12 MB", secondaryToEnd = true, showLinkArrow = true, @@ -135,7 +123,7 @@ fun SpotifySettingsScreen( item(key = "history") { SidekitItem( "Clear history", - Modifier.listFade(listState, 4), + Modifier.listFade(listState, 3), secondary = "${state.history.size} tracks", secondaryToEnd = true, showLinkArrow = true, @@ -145,19 +133,11 @@ fun SpotifySettingsScreen( item(key = "account") { SidekitItem( "Account", - Modifier.listFade(listState, 5), + Modifier.listFade(listState, 4), secondary = if (state.signedIn) "Signed in" else "Not signed in", secondaryToEnd = true ) } - item(key = "signout") { - SidekitItem( - "Sign out", - Modifier.listFade(listState, 6), - colour = ItemColour.Destructive, - onClick = { confirmation = SettingsConfirmation.SignOut } - ) - } } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 0d20fcaa..d1e52b5f 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -6,8 +6,10 @@ import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository +import hrln.interactive.sidekit.apps.spotify.data.SpotifyLibraryWriter import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifySession +import hrln.interactive.sidekit.ui.UndoWindow import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -20,6 +22,23 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.json.JSONObject +/** The last save/follow change, offered back by the action bar for a few seconds. */ +sealed interface SpotifyUndo { + val contentDescription: String + + data class Track(val id: String, val label: String?) : SpotifyUndo { + override val contentDescription: String = if (label == null) "Undo like change" else "Undo save to $label" + } + + data class Collection(val type: SpotifyCollectionType, val id: String) : SpotifyUndo { + override val contentDescription: String = "Undo library change" + } + + data class Artist(val id: String, val name: String) : SpotifyUndo { + override val contentDescription: String = "Undo follow change" + } +} + data class SpotifyUiState( val signedIn: Boolean = false, val current: SpotifyMediaItem? = null, @@ -48,8 +67,8 @@ data class SpotifyUiState( val searchScopes: Set = SearchScope.entries.toSet(), val query: String = "", val audioQuality: AudioQuality = AudioQuality.High, - val volume: Float = 0.72f, - val notice: String? = null + val notice: String? = null, + val undo: SpotifyUndo? = null ) { val savedCurrent: Boolean get() = current?.let { it.id in savedTrackIds } ?: false } @@ -61,6 +80,8 @@ data class SpotifyUiState( */ class SpotifyViewModel( private val catalog: SpotifyCatalog, + private val writer: SpotifyLibraryWriter? = null, + private val queue: SpotifyQueueRepository = SpotifyQueueRepository(), private val history: SpotifyHistoryRepository = SpotifyHistoryRepository(), private val session: SpotifySession? = null, private val openBrowser: (suspend (String) -> Unit)? = null, @@ -69,8 +90,16 @@ class SpotifyViewModel( private val _state = MutableStateFlow(SpotifyUiState(signedIn = session?.signedIn?.value ?: false)) val state: StateFlow = _state.asStateFlow() - private val queue = SpotifyQueueRepository() + /** The five-second offer; leaving the screen commits the change instead of carrying Undo away. */ + val undoWindow = UndoWindow(viewModelScope) { _state.update { it.copy(undo = null) } } + private var searchJob: Job? = null + private val savedTrackTouched = mutableSetOf() + private val savedCollectionTouched = mutableSetOf() + private val followedArtistTouched = mutableSetOf() + private val nextHandler = { next() } + private val previousHandler = { previous() } + private val seekHandler = { position: Long -> seekTo(position) } init { session?.let { @@ -80,7 +109,16 @@ class SpotifyViewModel( if (signed) loadAccount() else clearAccount() } } + viewModelScope.launch { + it.settings.collect { settings -> + _state.update { state -> state.copy(audioQuality = settings.audioQuality) } + } + } + it.playback.onNext = nextHandler + it.playback.onPrevious = previousHandler + it.playback.onSeek = seekHandler } + restorePlaybackState() if (session == null || !session.nativeAvailable) { // Local fixtures and builds without the native library still need a moving progress bar. viewModelScope.launch { @@ -100,6 +138,39 @@ class SpotifyViewModel( } } + /** Adopts the process-wide player and queue after the Sidetracks screen was recreated. */ + private fun restorePlaybackState() { + val session = session ?: return + val playback = session.playback + val snapshot = queue.snapshot + val current = playback.current ?: snapshot.current + if (current != null && snapshot.current?.id != current.id) { + queue.restore(snapshot.copy(current = current)) + } + val restored = queue.snapshot + _state.update { + it.copy( + current = current, + upcoming = restored.upcoming, + history = restored.history, + shuffle = restored.shuffle, + repeat = restored.repeat, + isPlaying = playback.isActive, + positionMs = playback.position, + recent = history.items().filterIsInstance() + ) + } + } + + override fun onCleared() { + session?.playback?.let { + if (it.onNext === nextHandler) it.onNext = null + if (it.onPrevious === previousHandler) it.onPrevious = null + if (it.onSeek === seekHandler) it.onSeek = null + } + super.onCleared() + } + fun dismissNotice() = _state.update { it.copy(notice = null) } fun refresh() = loadAccount() @@ -122,9 +193,15 @@ class SpotifyViewModel( viewModelScope.launch { try { val collection = catalog.collection(type, id) + val saved = if (collection == null) null else writer?.isSaved(type, id) _state.update { + val keys = it.savedCollectionKeys.toMutableSet() + if (saved != null && key !in savedCollectionTouched) { + if (saved) keys.add(key) else keys.remove(key) + } it.copy( collections = if (collection == null) it.collections else it.collections + (key to collection), + savedCollectionKeys = keys, pendingCollections = it.pendingCollections - key ) } @@ -151,11 +228,17 @@ class SpotifyViewModel( } val albums = catalog.albumsBy(artist) val tracks = catalog.topTracks(artist) + val followed = writer?.isArtistFollowed(id) _state.update { + val ids = it.followedArtistIds.toMutableSet() + if (followed != null && id !in followedArtistTouched) { + if (followed) ids.add(id) else ids.remove(id) + } it.copy( artists = it.artists + (id to artist), artistAlbums = it.artistAlbums + (id to albums), artistTracks = it.artistTracks + (id to tracks), + followedArtistIds = ids, pendingArtists = it.pendingArtists - id ) } @@ -176,7 +259,13 @@ class SpotifyViewModel( notice("Not available on this account") return } + if (_state.value.current?.id == item.id) { + if (!_state.value.isPlaying) togglePlayPause() + _state.update { it.copy(notice = null) } + return + } val snapshot = queue.play(items, index) + if (item is SpotifyTrack) ensureTrackSaved(item.id) recorded(item) applyQueue(snapshot, positionMs = 0L, playing = true) _state.update { it.copy(notice = null) } @@ -204,6 +293,7 @@ class SpotifyViewModel( viewModelScope.launch { try { if (play) session.resume() else session.pause() + session.playback.updateState(play, _state.value.positionMs) } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -264,43 +354,150 @@ class SpotifyViewModel( fun toggleSaveCurrent() { val item = _state.value.current ?: return - _state.update { - val saved = it.savedTrackIds.toMutableSet() - if (!saved.add(item.id)) saved.remove(item.id) - it.copy(savedTrackIds = saved, notice = if (item.id in saved) "Saved to Liked Songs" else "Removed from Liked Songs") - } + // A like is a simple toggle: no notice and no Undo offer. + toggleTrackSaved(item.id, null, offerUndo = false) } - fun toggleSaved(track: SpotifyTrack) = _state.update { - val saved = it.savedTrackIds.toMutableSet() - if (!saved.add(track.id)) saved.remove(track.id) - it.copy(savedTrackIds = saved) + fun toggleSaved(track: SpotifyTrack) = toggleTrackSaved(track.id, null) + + private fun toggleTrackSaved(id: String, label: String?, offerUndo: Boolean = true) { + val wasSaved = id in _state.value.savedTrackIds + val saved = !wasSaved + val action = SpotifyUndo.Track(id, label) + savedTrackTouched += id + _state.update { + val ids = it.savedTrackIds.toMutableSet() + if (saved) ids.add(id) else ids.remove(id) + it.copy( + savedTrackIds = ids, + notice = label?.let { name -> if (saved) "Saved to $name" else "Removed from $name" }, + undo = if (offerUndo) action else it.undo + ) + } + if (offerUndo) undoWindow.start() + val writer = writer ?: return + viewModelScope.launch { + try { + writer.setTrackSaved(id, saved) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + clearUndo(action) + _state.update { + val ids = it.savedTrackIds.toMutableSet() + if (wasSaved) ids.add(id) else ids.remove(id) + it.copy(savedTrackIds = ids, notice = playbackNotice(e)) + } + } + } } fun savedCollection(collection: SpotifyCollection): Boolean = collectionKey(collection.type, collection.id) in _state.value.savedCollectionKeys - fun toggleSaved(collection: SpotifyCollection) = _state.update { - val keys = it.savedCollectionKeys.toMutableSet() - val key = collectionKey(collection.type, collection.id) - val wasSaved = keys.contains(key) - if (!keys.add(key)) keys.remove(key) - it.copy( - savedCollectionKeys = keys, - notice = when (collection.type) { - SpotifyCollectionType.Album -> if (wasSaved) "Removed from Library" else "Saved to Library" - SpotifyCollectionType.Playlist -> if (wasSaved) "Removed from Library" else "Saved to Library" - SpotifyCollectionType.Show -> if (wasSaved) "Removed from Library" else "Saved to Library" + fun toggleSaved(collection: SpotifyCollection) = toggleSaved(collection.type, collection.id) + + private fun toggleSaved(type: SpotifyCollectionType, id: String, offerUndo: Boolean = true) { + val key = collectionKey(type, id) + val wasSaved = key in _state.value.savedCollectionKeys + val saved = !wasSaved + val action = SpotifyUndo.Collection(type, id) + savedCollectionTouched += key + _state.update { + val keys = it.savedCollectionKeys.toMutableSet() + if (saved) keys.add(key) else keys.remove(key) + it.copy( + savedCollectionKeys = keys, + notice = if (saved) "Saved to Library" else "Removed from Library", + undo = if (offerUndo) action else it.undo + ) + } + if (offerUndo) undoWindow.start() + val writer = writer ?: return + viewModelScope.launch { + try { + writer.setSaved(type, id, saved) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + clearUndo(action) + _state.update { + val keys = it.savedCollectionKeys.toMutableSet() + if (wasSaved) keys.add(key) else keys.remove(key) + it.copy(savedCollectionKeys = keys, notice = playbackNotice(e)) + } } - ) + } } fun followed(artist: SpotifyArtist): Boolean = artist.id in _state.value.followedArtistIds - fun toggleFollow(artist: SpotifyArtist) = _state.update { - val ids = it.followedArtistIds.toMutableSet() - val wasFollowing = !ids.add(artist.id) - if (wasFollowing) ids.remove(artist.id) - it.copy(followedArtistIds = ids, notice = if (wasFollowing) "Unfollowed ${artist.name}" else "Following ${artist.name}") + fun toggleFollow(artist: SpotifyArtist) = toggleFollow(artist.id, artist.name) + + private fun toggleFollow(id: String, name: String, offerUndo: Boolean = true) { + val wasFollowing = id in _state.value.followedArtistIds + val following = !wasFollowing + val action = SpotifyUndo.Artist(id, name) + followedArtistTouched += id + _state.update { + val ids = it.followedArtistIds.toMutableSet() + if (following) ids.add(id) else ids.remove(id) + it.copy( + followedArtistIds = ids, + notice = if (following) "Following $name" else "Unfollowed $name", + undo = if (offerUndo) action else it.undo + ) + } + if (offerUndo) undoWindow.start() + val writer = writer ?: return + viewModelScope.launch { + try { + writer.setArtistFollowed(id, following) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + clearUndo(action) + _state.update { + val ids = it.followedArtistIds.toMutableSet() + if (wasFollowing) ids.add(id) else ids.remove(id) + it.copy(followedArtistIds = ids, notice = playbackNotice(e)) + } + } + } + } + + fun undo() { + val action = _state.value.undo ?: return + undoWindow.cancel() + _state.update { it.copy(undo = null) } + when (action) { + is SpotifyUndo.Track -> toggleTrackSaved(action.id, action.label, offerUndo = false) + is SpotifyUndo.Collection -> toggleSaved(action.type, action.id, offerUndo = false) + is SpotifyUndo.Artist -> toggleFollow(action.id, action.name, offerUndo = false) + } + } + + /** Leaving the screen finalises the change instead of offering Undo when you come back. */ + fun commitUndo() { + undoWindow.cancel() + _state.update { it.copy(undo = null) } + } + + private fun clearUndo(action: SpotifyUndo) { + if (_state.value.undo == action) { + undoWindow.cancel() + _state.update { it.copy(undo = null) } + } + } + + private fun ensureTrackSaved(id: String) { + val writer = writer ?: return + if (id in _state.value.savedTrackIds || id in savedTrackTouched) return + viewModelScope.launch { + val saved = runCatching { writer.isTrackSaved(id) }.getOrNull() ?: return@launch + if (saved && id !in savedTrackTouched) { + _state.update { it.copy(savedTrackIds = it.savedTrackIds + id) } + } + } } fun setFilter(filter: LibraryFilter) { @@ -326,14 +523,10 @@ class SpotifyViewModel( search(debounceMs = 0L) } - fun cycleAudioQuality() = _state.update { it.copy(audioQuality = it.audioQuality.next()) } - - fun setVolume(value: Float) { - val volume = value.coerceIn(0f, 1f) - _state.update { it.copy(volume = volume) } - session?.let { session -> - viewModelScope.launch { runCatching { session.setVolume(volume) } } - } + fun cycleAudioQuality() { + val next = _state.value.audioQuality.next() + _state.update { it.copy(audioQuality = next) } + session?.setAudioQuality(next) } fun clearHistory() { @@ -350,17 +543,14 @@ class SpotifyViewModel( fun moveToNext(item: SpotifyMediaItem) { applyQueue(queue.playNext(item)) - _state.update { it.copy(notice = "Playing next") } } fun removeUpcoming(item: SpotifyMediaItem) { applyQueue(queue.removeUpcoming(item)) - _state.update { it.copy(notice = "Removed from queue") } } fun addToQueue(item: SpotifyMediaItem) { applyQueue(queue.addToQueue(item)) - _state.update { it.copy(notice = "Added to queue") } } fun clearUpcoming(): List { @@ -420,10 +610,11 @@ class SpotifyViewModel( _state.update { it.copy(libraryLoading = true) } viewModelScope.launch { try { + val filter = _state.value.filter val recent = catalog.recentlyPlayed() - val library = catalog.library(_state.value.filter) + val library = catalog.library(filter) _state.update { - it.copy( + it.withLibrarySaved(filter, library).copy( recent = recent.ifEmpty { history.items().filterIsInstance() }, libraryItems = library, libraryLoading = false @@ -444,7 +635,11 @@ class SpotifyViewModel( try { val items = catalog.library(filter) _state.update { - if (it.filter == filter) it.copy(libraryItems = items, libraryLoading = false) else it + if (it.filter == filter) { + it.withLibrarySaved(filter, items).copy(libraryItems = items, libraryLoading = false) + } else { + it + } } } catch (e: CancellationException) { throw e @@ -454,35 +649,69 @@ class SpotifyViewModel( } } - private fun clearAccount() = _state.update { - it.copy( - recent = emptyList(), - libraryItems = emptyList(), - searchResults = SpotifySearchResults(), - collections = emptyMap(), - artists = emptyMap(), - artistAlbums = emptyMap(), - artistTracks = emptyMap(), - savedTrackIds = emptySet(), - savedCollectionKeys = emptySet(), - followedArtistIds = emptySet() + private fun clearAccount() { + undoWindow.cancel() + queue.stop() + history.clear() + savedTrackTouched.clear() + savedCollectionTouched.clear() + followedArtistTouched.clear() + _state.update { + it.copy( + recent = emptyList(), + libraryItems = emptyList(), + searchResults = SpotifySearchResults(), + collections = emptyMap(), + artists = emptyMap(), + artistAlbums = emptyMap(), + artistTracks = emptyMap(), + savedTrackIds = emptySet(), + savedCollectionKeys = emptySet(), + followedArtistIds = emptySet() + ) + } + } + + private fun SpotifyUiState.withLibrarySaved(filter: LibraryFilter, items: List): SpotifyUiState = when (filter) { + LibraryFilter.LikedSongs -> copy(savedTrackIds = items.filterIsInstance().map { it.id }.toSet()) + + LibraryFilter.Albums -> copy( + savedCollectionKeys = savedCollectionKeys.withoutPrefix("${SpotifyCollectionType.Album.route}:") + + items.filterIsInstance().map { collectionKey(SpotifyCollectionType.Album, it.id) } + ) + + LibraryFilter.Playlists -> copy( + savedCollectionKeys = savedCollectionKeys.withoutPrefix("${SpotifyCollectionType.Playlist.route}:") + + items.filterIsInstance().map { collectionKey(SpotifyCollectionType.Playlist, it.id) } + ) + + LibraryFilter.Shows -> copy( + savedCollectionKeys = savedCollectionKeys.withoutPrefix("${SpotifyCollectionType.Show.route}:") + + items.filterIsInstance().map { collectionKey(SpotifyCollectionType.Show, it.id) } ) + + LibraryFilter.Artists -> copy(followedArtistIds = items.filterIsInstance().map { it.id }.toSet()) } + private fun Set.withoutPrefix(prefix: String): Set = filterNot { it.startsWith(prefix) }.toSet() + private fun applyQueue( snapshot: SpotifyQueueRepository.Snapshot, positionMs: Long? = null, playing: Boolean? = null - ) = _state.update { - it.copy( - current = snapshot.current, - upcoming = snapshot.upcoming, - history = snapshot.history, - shuffle = snapshot.shuffle, - repeat = snapshot.repeat, - positionMs = positionMs ?: it.positionMs, - isPlaying = playing ?: it.isPlaying - ) + ) { + _state.update { + it.copy( + current = snapshot.current, + upcoming = snapshot.upcoming, + history = snapshot.history, + shuffle = snapshot.shuffle, + repeat = snapshot.repeat, + positionMs = positionMs ?: it.positionMs, + isPlaying = playing ?: it.isPlaying + ) + } + session?.playback?.setNowPlaying(snapshot.current, positionMs ?: _state.value.positionMs) } private fun recorded(item: SpotifyMediaItem) { @@ -534,10 +763,12 @@ class SpotifyViewModel( "playing" -> if (matchesCurrent(event)) { _state.update { it.copy(isPlaying = true, positionMs = position) } + session?.playback?.updateState(true, position) } "paused" -> if (matchesCurrent(event)) { _state.update { it.copy(isPlaying = false, positionMs = position) } + session?.playback?.updateState(false, position) } "position" -> _state.update { it.copy(positionMs = position) } @@ -612,6 +843,7 @@ class SpotifyViewModel( private fun startRemote(item: SpotifyMediaItem, positionMs: Long) { val uri = item.uri ?: return val session = session ?: return + session.playback.setNowPlaying(item, positionMs) viewModelScope.launch { try { session.play(uri, positionMs) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index e2c07ee0..d25be4d1 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -25,7 +25,7 @@ object NativeBridge : SpotifyNative { if (isAvailable) runCatching { nativeDisconnect() } } - override fun createPlayer(): Boolean = isAvailable && runCatching { nativeCreatePlayer() }.getOrDefault(false) + override fun createPlayer(bitrateKbps: Int): Boolean = isAvailable && runCatching { nativeCreatePlayer(bitrateKbps) }.getOrDefault(false) override fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean = isAvailable && runCatching { nativeLoad(uri, positionMs, autoplay) }.getOrDefault(false) @@ -37,10 +37,6 @@ object NativeBridge : SpotifyNative { override fun seek(positionMs: Long): Boolean = isAvailable && runCatching { nativeSeek(positionMs) }.getOrDefault(false) - override fun setVolume(volume: Float) { - if (isAvailable) runCatching { nativeSetVolume(volume) } - } - override fun readAudio(timeoutMs: Long): ByteArray? = if (isAvailable) runCatching { nativeReadAudio(timeoutMs) }.getOrNull() else null override fun audioFormat(): String? = if (isAvailable) runCatching { nativeAudioFormat() }.getOrNull() else null @@ -59,18 +55,25 @@ object NativeBridge : SpotifyNative { override fun catalogSearch(query: String, scopes: String, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogSearch(query, scopes, limit) }.getOrNull() else null + override fun collectionContains(set: String, uris: String): String? = if (isAvailable) runCatching { nativeCollectionContains(set, uris) }.getOrNull() else null + + override fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean = isAvailable && runCatching { nativeCollectionWrite(set, uris, removed) }.getOrDefault(false) + + override fun playlistSaved(id: String): String? = if (isAvailable) runCatching { nativePlaylistSaved(id) }.getOrNull() else null + + override fun playlistSave(id: String, save: Boolean): Boolean = isAvailable && runCatching { nativePlaylistSave(id, save) }.getOrDefault(false) + private external fun nativeInit(): Boolean private external fun nativeSetTmpDir(path: String) private external fun nativeConnect(accessToken: String): Boolean private external fun nativeSetAccessToken(accessToken: String): Boolean private external fun nativeDisconnect() - private external fun nativeCreatePlayer(): Boolean + private external fun nativeCreatePlayer(bitrateKbps: Int): Boolean private external fun nativeLoad(uri: String, positionMs: Long, autoplay: Boolean): Boolean private external fun nativePlay(): Boolean private external fun nativePause(): Boolean private external fun nativeStop(): Boolean private external fun nativeSeek(positionMs: Long): Boolean - private external fun nativeSetVolume(volume: Float) private external fun nativeReadAudio(timeoutMs: Long): ByteArray? private external fun nativeAudioFormat(): String? private external fun nativePollEvent(): String? @@ -80,4 +83,8 @@ object NativeBridge : SpotifyNative { private external fun nativeCatalogAlbumsBy(id: String): String? private external fun nativeCatalogTopTracks(id: String): String? private external fun nativeCatalogSearch(query: String, scopes: String, limit: Long): String? + private external fun nativeCollectionContains(set: String, uris: String): String? + private external fun nativeCollectionWrite(set: String, uris: String, removed: Boolean): Boolean + private external fun nativePlaylistSaved(id: String): String? + private external fun nativePlaylistSave(id: String, save: Boolean): Boolean } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt index 7e84a902..0c8e5cdc 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -11,13 +11,12 @@ interface SpotifyNative { fun connect(accessToken: String): Boolean fun setAccessToken(accessToken: String): Boolean fun disconnect() - fun createPlayer(): Boolean + fun createPlayer(bitrateKbps: Int): Boolean fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean fun play(): Boolean fun pause(): Boolean fun stop(): Boolean fun seek(positionMs: Long): Boolean - fun setVolume(volume: Float) fun readAudio(timeoutMs: Long): ByteArray? fun audioFormat(): String? fun pollEvent(): String? @@ -28,4 +27,9 @@ interface SpotifyNative { fun catalogAlbumsBy(id: String): String? fun catalogTopTracks(id: String): String? fun catalogSearch(query: String, scopes: String, limit: Long): String? + + fun collectionContains(set: String, uris: String): String? + fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean + fun playlistSaved(id: String): String? + fun playlistSave(id: String, save: Boolean): Boolean } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt index 34792e84..39fe80b0 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt @@ -26,7 +26,8 @@ import org.json.JSONObject class NativeSpotifyCatalog( private val native: SpotifyNative, private val ensureSession: suspend () -> Unit -) : SpotifyCatalog { +) : SpotifyCatalog, + SpotifyLibraryWriter { /** The protocol has no safe recently-played endpoint; the ViewModel falls back to local history. */ override suspend fun recentlyPlayed(): List = emptyList() @@ -73,6 +74,58 @@ class NativeSpotifyCatalog( ) } + override suspend fun isTrackSaved(id: String): Boolean? = contains("collection", "spotify:track:$id") + + override suspend fun isSaved(type: SpotifyCollectionType, id: String): Boolean? = when (type) { + SpotifyCollectionType.Album -> contains("collection", "spotify:album:$id") + SpotifyCollectionType.Show -> contains("show", "spotify:show:$id") + SpotifyCollectionType.Playlist -> playlistSaved(id) + } + + override suspend fun isArtistFollowed(id: String): Boolean? = contains("artist", "spotify:artist:$id") + + override suspend fun setTrackSaved(id: String, saved: Boolean) = writeCollection("collection", "spotify:track:$id", saved) + + override suspend fun setSaved(type: SpotifyCollectionType, id: String, saved: Boolean) { + when (type) { + SpotifyCollectionType.Album -> writeCollection("collection", "spotify:album:$id", saved) + SpotifyCollectionType.Show -> writeCollection("show", "spotify:show:$id", saved) + SpotifyCollectionType.Playlist -> writePlaylist(id, saved) + } + } + + override suspend fun setArtistFollowed(id: String, followed: Boolean) = writeCollection("artist", "spotify:artist:$id", followed) + + private suspend fun contains(set: String, uri: String): Boolean? { + val text = callText { + native.collectionContains(set, JSONArray(listOf(uri)).toString()) + } + return runCatching { JSONArray(text).optBoolean(0) }.getOrNull() + } + + private suspend fun playlistSaved(id: String): Boolean? { + val text = callText { native.playlistSaved(id) } + return runCatching { text.toBooleanStrict() }.getOrNull() + } + + private suspend fun writeCollection(set: String, uri: String, saved: Boolean) { + withContext(Dispatchers.IO) { + ensureSession() + if (!native.collectionWrite(set, JSONArray(listOf(uri)).toString(), !saved)) { + fail(SpotifyFailure.Network, "Spotify didn't accept the library change") + } + } + } + + private suspend fun writePlaylist(id: String, saved: Boolean) { + withContext(Dispatchers.IO) { + ensureSession() + if (!native.playlistSave(id, saved)) { + fail(SpotifyFailure.Network, "Spotify didn't accept the library change") + } + } + } + private suspend fun call(block: () -> String?): JSONObject { val text = callText(block) return runCatching { JSONObject(text) }.getOrElse { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt index 9f063f06..363826da 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt @@ -10,6 +10,7 @@ import kotlinx.coroutines.withContext */ class NativeSpotifyGateway( private val native: SpotifyNative, + private val settings: () -> SpotifySettings = { SpotifySettings() }, private val accessToken: suspend () -> String? ) : SpotifyGateway { @Volatile @@ -25,12 +26,7 @@ class NativeSpotifyGateway( requireNative() if (connected) return@onIo connectSession(accessToken) - if (!native.createPlayer()) { - native.disconnect() - sessionConnected = false - fail(SpotifyFailure.Unknown, "Could not create the player") - } - connected = true + createPlayer() } override suspend fun disconnect() = onIo { @@ -65,11 +61,6 @@ class NativeSpotifyGateway( if (!native.seek(positionMs)) fail(SpotifyFailure.NotConnected, "Playback is not ready") } - override suspend fun setVolume(volume: Float) = onIo { - requireNative() - native.setVolume(volume) - } - /** Forgets the native session after it has been torn down outside this gateway. */ fun reset() { connected = false @@ -80,6 +71,21 @@ class NativeSpotifyGateway( /** Connects with the current stored token, refreshing it first when needed. */ suspend fun ensureConnected() { val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + if (!connected) { + connect(token) + return + } + if (token == sessionToken) return + // The OAuth token rotates every hour. Mutate the existing session instead of reconnecting + // unless native code reports that the session itself has gone away. + val updated = withContext(Dispatchers.IO) { native.setAccessToken(token) } + if (updated) { + sessionToken = token + return + } + connected = false + sessionConnected = false + sessionToken = null connect(token) } @@ -108,6 +114,17 @@ class NativeSpotifyGateway( sessionToken = token } + private fun createPlayer() { + val current = settings() + if (!native.createPlayer(current.bitrateKbps)) { + native.disconnect() + sessionConnected = false + sessionToken = null + fail(SpotifyFailure.Unknown, "Could not create the player") + } + connected = true + } + private fun requireNative() { if (!native.isAvailable) fail(SpotifyFailure.Unavailable, "Spotify playback is unavailable in this build") } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt index d055d2b3..54a8d390 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt @@ -22,3 +22,16 @@ interface SpotifyCatalog { suspend fun topTracks(artist: SpotifyArtist): List suspend fun search(query: String, scopes: Set): SpotifySearchResults } + +/** + * Write side of the user's Spotify library. Kept separate from [SpotifyCatalog] so read-only + * fallbacks and test catalogs do not have to pretend they can mutate an account. + */ +interface SpotifyLibraryWriter { + suspend fun isTrackSaved(id: String): Boolean? + suspend fun isSaved(type: SpotifyCollectionType, id: String): Boolean? + suspend fun isArtistFollowed(id: String): Boolean? + suspend fun setTrackSaved(id: String, saved: Boolean) + suspend fun setSaved(type: SpotifyCollectionType, id: String, saved: Boolean) + suspend fun setArtistFollowed(id: String, followed: Boolean) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt index 2ed30c63..4d0f16e6 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyGateway.kt @@ -13,7 +13,6 @@ interface SpotifyGateway { suspend fun pause() suspend fun stop() suspend fun seek(positionMs: Long) - suspend fun setVolume(volume: Float) } /** Stable gateway failures. Raw librespot strings must not reach the UI. */ diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt index 3e29a013..874e6998 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt @@ -25,6 +25,12 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { var snapshot: Snapshot = Snapshot() private set + /** Replaces the queue, used when a new ViewModel adopts the process-wide playback state. */ + fun restore(snapshot: Snapshot): Snapshot { + this.snapshot = snapshot + return snapshot + } + private var shuffleBackup: List? = null /** diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 64c3b499..f801b398 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -1,6 +1,7 @@ package hrln.interactive.sidekit.apps.spotify.data import android.content.Context +import hrln.interactive.sidekit.apps.spotify.AudioQuality import hrln.interactive.sidekit.apps.spotify.auth.EncryptedSpotifyCredentialStore import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge @@ -12,6 +13,8 @@ import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext /** @@ -20,15 +23,20 @@ import kotlinx.coroutines.withContext */ class SpotifySession( private val auth: SpotifyAuth = SpotifyAuth(NoopCredentialStore), - private val tmpDir: String? = null + private val tmpDir: String? = null, + context: Context? = null ) { private val _signedIn = MutableStateFlow(auth.hasCredentials()) val signedIn: StateFlow = _signedIn.asStateFlow() + private val settingsStore = context?.let { SpotifySettingsStore(it.applicationContext) } + private val _settings = MutableStateFlow(settingsStore?.read() ?: SpotifySettings()) + val settings: StateFlow = _settings.asStateFlow() + val nativeAvailable: Boolean get() = NativeBridge.isAvailable private val nativeGateway by lazy { - NativeSpotifyGateway(NativeBridge) { accessToken() } + NativeSpotifyGateway(NativeBridge, { _settings.value }, { accessToken() }) } val gateway: SpotifyGateway get() = nativeGateway @@ -36,13 +44,19 @@ class SpotifySession( val catalog: SpotifyCatalog by lazy { if (nativeAvailable) NativeSpotifyCatalog(NativeBridge) { nativeGateway.ensureSession() } else SpotifyWebCatalog(webApi) } + val writer: SpotifyLibraryWriter? get() = catalog as? SpotifyLibraryWriter + + /** Process-wide queue and history so leaving Sidetracks does not forget what is playing. */ + val queue = SpotifyQueueRepository() + val history = SpotifyHistoryRepository() private val playbackScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) val playback: SpotifyPlayback by lazy { SpotifyPlayback( gateway = nativeGateway, scope = playbackScope, - audio = SpotifyAudioSink(NativeBridge, playbackScope) + audio = SpotifyAudioSink(NativeBridge, playbackScope), + context = context?.applicationContext ) } @@ -59,8 +73,19 @@ class SpotifySession( } fun signOut() { - nativeGateway.reset() - NativeBridge.disconnect() + queue.stop() + history.clear() + if (nativeAvailable) { + playback.setNowPlaying(null) + playback.pause() + // Stop the decoder immediately; waiting for the coroutine below leaves a window in + // which the current track keeps playing after the credentials have been cleared. + NativeBridge.stop() + playbackScope.launch { runCatching { playback.disconnect() } } + } else { + nativeGateway.reset() + NativeBridge.disconnect() + } auth.clear() _signedIn.value = false } @@ -84,7 +109,22 @@ class SpotifySession( suspend fun accessToken(): String? = auth.accessToken() - suspend fun setVolume(volume: Float) = playback.setVolume(volume) + fun setAudioQuality(quality: AudioQuality) { + _settings.update { it.copy(audioQuality = quality) } + settingsStore?.write(_settings.value) + } + + fun togglePlayback() { + if (playback.isActive) { + playback.pause() + } else { + playbackScope.launch { runCatching { resume() } } + } + } + + fun stopPlayback() { + playbackScope.launch { runCatching { playback.stop() } } + } /** One librespot player event, or null when nothing has happened since the last poll. */ fun pollPlaybackEvent(): String? = if (nativeAvailable) NativeBridge.pollEvent() else null @@ -104,7 +144,8 @@ object SpotifySessions { fun get(context: Context): SpotifySession = instance ?: synchronized(this) { instance ?: SpotifySession( SpotifyAuth(EncryptedSpotifyCredentialStore(context.applicationContext)), - context.applicationContext.cacheDir.absolutePath + context.applicationContext.cacheDir.absolutePath, + context.applicationContext ).also { instance = it } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt new file mode 100644 index 00000000..918aa4d2 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt @@ -0,0 +1,44 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import android.content.Context +import hrln.interactive.sidekit.apps.integrations.PrivateSettings +import hrln.interactive.sidekit.apps.spotify.AudioQuality + +/** User preferences that survive process death and apply before the native player is created. */ +data class SpotifySettings( + val audioQuality: AudioQuality = AudioQuality.High +) { + val bitrateKbps: Int get() = audioQuality.bitrateKbps +} + +/** + * Settings live in the same encrypted, atomic store as credentials. They are not secrets, but the + * store already exists, needs no extra dependency and keeps all Sidetracks state in one place. + */ +class SpotifySettingsStore( + context: Context, + name: String = "sidetracks_settings" +) { + private val settings = PrivateSettings(context.applicationContext, name) + + @Synchronized + fun read(): SpotifySettings { + val value = settings.read() + val quality = AudioQuality.entries + .firstOrNull { it.name == value.optString(KEY_QUALITY) } + ?: AudioQuality.High + return SpotifySettings(audioQuality = quality) + } + + @Synchronized + fun write(value: SpotifySettings) { + settings.write( + org.json.JSONObject() + .put(KEY_QUALITY, value.audioQuality.name) + ) + } + + private companion object { + const val KEY_QUALITY = "audio_quality" + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index 446eb610..845ef378 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -1,15 +1,30 @@ package hrln.interactive.sidekit.apps.spotify.playback +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Bitmap +import android.media.AudioAttributes +import android.media.AudioManager +import android.media.MediaMetadata +import android.media.session.MediaSession +import android.media.session.PlaybackState as SessionState +import android.os.SystemClock +import hrln.interactive.sidekit.apps.media.AudioFocus import hrln.interactive.sidekit.apps.media.MediaSessionOwner import hrln.interactive.sidekit.apps.media.MediaSessionRegistry import hrln.interactive.sidekit.apps.media.MediaSessions +import hrln.interactive.sidekit.apps.media.RemoteArtwork +import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem import hrln.interactive.sidekit.apps.spotify.data.SpotifyGateway import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.launch /** * Process-scoped playback owner. The activity and screens may come and go; this object holds the - * native player lifetime and claims the one media session while it is producing audio. + * native player lifetime, the platform media session and the foreground notification. * * Main-thread only, like [MediaSessionOwner]. Long-running gateway work is moved onto [scope]. */ @@ -17,63 +32,276 @@ class SpotifyPlayback( private val gateway: SpotifyGateway, private val scope: CoroutineScope, private val registry: MediaSessionRegistry = MediaSessions, - private val audio: SpotifyAudioSink? = null + private val audio: SpotifyAudioSink? = null, + private val context: Context? = null ) : MediaSessionOwner { override val sessionId = "sidetracks" override val notificationChannelId = "sidetracks" - override val notificationChannelName = "Sidetracks" + override val notificationChannelName = "Sidetracks playback" override val notificationId = 0x6D76 @Volatile private var playing = false + @Volatile + private var positionMs = 0L + + @Volatile + var current: SpotifyMediaItem? = null + private set + + val position: Long get() = positionMs + + @Volatile + var notificationArt: Bitmap? = null + private set + override val isActive: Boolean get() = playing + /** Queue ops still live in the ViewModel; the notification and lock screen call back into it. */ + var onNext: (() -> Unit)? = null + var onPrevious: (() -> Unit)? = null + var onSeek: ((Long) -> Unit)? = null + + private var mediaSession: MediaSession? = null + private var artJob: Job? = null + private var lastSessionUpdate = 0L + private var noisyRegistered = false + + private val noisy = object : BroadcastReceiver() { + // Headphones unplugged: pause rather than play out loud. + override fun onReceive(context: Context, intent: Intent) = pause() + } + + private val focus = context?.let { + AudioFocus( + it, + AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) + .build(), + onPause = { pauseInternal(abandonFocus = false) }, + onResume = { scope.launch { runCatching { resume() } } } + ) + } + + val sessionToken: MediaSession.Token? get() = session()?.sessionToken + /** Registers once so starting local Music can pause this owner even before the first track. */ fun register() = registry.register(this) + fun setNowPlaying(item: SpotifyMediaItem?, position: Long = 0L) { + current = item + positionMs = position + artJob?.cancel() + notificationArt = null + if (context != null) updateMetadata() + publish() + if (item?.artUrl != null && context != null) { + artJob = scope.launch { + val bitmap = RemoteArtwork.load(context, item.artUrl!!, ART_PX, ART_PX) + if (current?.id == item.id) { + notificationArt = bitmap + lastSessionUpdate = 0L + updateMetadata() + publish() + } + } + } + } + + /** Mirrors native player events into the media session and notification. */ + fun updateState(isPlaying: Boolean, position: Long) { + playing = isPlaying + positionMs = position + publish() + } + suspend fun start(uri: String, positionMs: Long) { register() + if (focus?.request() == false) return registry.activate(this) playing = true - audio?.start() + this.positionMs = positionMs + // Release the previous AudioTrack before loading the next item. Reusing a live sink can + // leave the old track's buffered PCM playing underneath the new one. + audio?.pause() + publish() try { gateway.load(uri, positionMs, autoplay = true) + audio?.start() } catch (t: Throwable) { playing = false audio?.pause() + focus?.abandon() registry.deactivate(this) + publish() throw t } } suspend fun resume() { + register() + if (focus?.request() == false) return registry.activate(this) playing = true audio?.start() + publish() gateway.play() } - override fun pause() { + fun togglePlayPause() { + if (playing) pause() else scope.launch { runCatching { resume() } } + } + + override fun pause() = pauseInternal(abandonFocus = true) + + /** + * Transient audio-focus loss keeps the focus request held so [AudioFocus] can resume us when + * the call or notification ends. An explicit pause gives it back. + */ + private fun pauseInternal(abandonFocus: Boolean) { playing = false audio?.pause() + if (abandonFocus) focus?.abandon() registry.deactivate(this) + publish() scope.launch { runCatching { gateway.pause() } } } - suspend fun seek(positionMs: Long) = gateway.seek(positionMs) + suspend fun seek(positionMs: Long) { + this.positionMs = positionMs + publish() + gateway.seek(positionMs) + } + + fun requestSeek(positionMs: Long) { + val callback = onSeek + if (callback != null) callback(positionMs) else scope.launch { runCatching { seek(positionMs) } } + } + + fun requestNext() = onNext?.invoke() - suspend fun setVolume(volume: Float) = gateway.setVolume(volume) + fun requestPrevious() = onPrevious?.invoke() suspend fun stop() { playing = false audio?.pause() + focus?.abandon() registry.deactivate(this) - gateway.stop() + runCatching { gateway.stop() } + mediaSession?.let { + it.setPlaybackState( + SessionState.Builder() + .setState(SessionState.STATE_STOPPED, 0L, 0f, SystemClock.elapsedRealtime()) + .build() + ) + } + current = null + notificationArt = null + artJob?.cancel() + publish() } suspend fun disconnect() { stop() gateway.disconnect() + mediaSession?.release() + mediaSession = null + } + + private fun publish() { + session()?.let { session -> + val now = SystemClock.elapsedRealtime() + if (current != null) updateMetadata(session, now) + session.setPlaybackState( + SessionState.Builder() + .setActions( + SessionState.ACTION_PLAY or SessionState.ACTION_PAUSE or + SessionState.ACTION_PLAY_PAUSE or SessionState.ACTION_SKIP_TO_NEXT or + SessionState.ACTION_SKIP_TO_PREVIOUS or SessionState.ACTION_SEEK_TO or + SessionState.ACTION_STOP + ) + .setState( + when { + current == null -> SessionState.STATE_STOPPED + playing -> SessionState.STATE_PLAYING + else -> SessionState.STATE_PAUSED + }, + positionMs, + if (playing) 1f else 0f, + now + ) + .build() + ) + } + updateNoisy() + context?.let { SpotifyPlaybackService.sync(it, this) } + } + + private fun updateNoisy() { + val context = context ?: return + if (playing == noisyRegistered) return + noisyRegistered = playing + if (playing) { + context.registerReceiver(noisy, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) + } else { + runCatching { context.unregisterReceiver(noisy) } + } + } + + private fun updateMetadata(session: MediaSession? = session(), now: Long = SystemClock.elapsedRealtime()) { + val item = current ?: return + session ?: return + if (lastSessionUpdate != 0L && now - lastSessionUpdate < 250L) return + lastSessionUpdate = now + session.setMetadata( + MediaMetadata.Builder() + .putString(MediaMetadata.METADATA_KEY_TITLE, item.title) + .putString(MediaMetadata.METADATA_KEY_ARTIST, item.creator) + .putString(MediaMetadata.METADATA_KEY_ALBUM, item.creator) + .putLong(MediaMetadata.METADATA_KEY_DURATION, item.durationMs) + .apply { notificationArt?.let { putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, it) } } + .build() + ) + } + + private fun session(): MediaSession? { + val context = context ?: return null + return mediaSession ?: MediaSession(context, "SideKit Sidetracks").also { session -> + session.setCallback( + object : MediaSession.Callback() { + override fun onPlay() { + scope.launch { runCatching { resume() } } + } + + override fun onPause() { + pause() + } + + override fun onSkipToNext() { + requestNext() + } + + override fun onSkipToPrevious() { + requestPrevious() + } + + override fun onSeekTo(pos: Long) { + requestSeek(pos) + } + + override fun onStop() { + scope.launch { runCatching { stop() } } + } + } + ) + session.isActive = true + mediaSession = session + } + } + + private companion object { + const val ART_PX = 256 } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt new file mode 100644 index 00000000..c11c4176 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt @@ -0,0 +1,165 @@ +package hrln.interactive.sidekit.apps.spotify.playback + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.Service +import android.content.Context +import android.content.Intent +import android.content.pm.ServiceInfo +import android.graphics.drawable.Icon +import android.os.IBinder +import hrln.interactive.sidekit.MainActivity +import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions +import hrln.interactive.sidekit.design.icons.SidekitIcon + +/** + * Keeps Sidetracks playing when SideKit is not in front. It mirrors [SpotifyPlayback]'s media + * session in a platform media-style notification and forwards transport actions back to the + * process-wide player. + */ +class SpotifyPlaybackService : Service() { + override fun onBind(intent: Intent?): IBinder? = null + + override fun onCreate() { + super.onCreate() + running = this + val playback = SpotifySessions.get(this).playback + val manager = getSystemService(NotificationManager::class.java) + if (manager.getNotificationChannel(playback.notificationChannelId) == null) { + manager.createNotificationChannel( + NotificationChannel( + playback.notificationChannelId, + playback.notificationChannelName, + NotificationManager.IMPORTANCE_LOW + ).apply { setShowBadge(false) } + ) + } + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val session = SpotifySessions.get(this) + val playback = session.playback + startForeground( + playback.notificationId, + notification(playback, playing = playback.isActive), + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + ) + when (intent?.action) { + ACTION_PLAY_PAUSE -> session.togglePlayback() + ACTION_NEXT -> playback.requestNext() + ACTION_PREVIOUS -> playback.requestPrevious() + ACTION_STOP -> session.stopPlayback() + } + update(playback) + return START_NOT_STICKY + } + + override fun onDestroy() { + if (running === this) running = null + super.onDestroy() + } + + private fun update(playback: SpotifyPlayback) { + when { + playback.current == null -> { + stopForeground(STOP_FOREGROUND_REMOVE) + stopSelf() + } + + playback.isActive -> startForeground( + playback.notificationId, + notification(playback, playing = true), + ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + ) + + else -> { + getSystemService(NotificationManager::class.java) + .notify(playback.notificationId, notification(playback, playing = false)) + stopForeground(STOP_FOREGROUND_DETACH) + } + } + } + + private fun notification(playback: SpotifyPlayback, playing: Boolean): Notification { + val open = PendingIntent.getActivity( + this, + 0, + Intent(this, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + PendingIntent.FLAG_IMMUTABLE + ) + return Notification.Builder(this, playback.notificationChannelId) + .setSmallIcon(SidekitIcon.MusicNote2.drawable) + .setContentTitle(playback.current?.title ?: "Sidetracks") + .setContentText(playback.current?.creator.orEmpty()) + .setLargeIcon(playback.notificationArt) + .setContentIntent(open) + .setDeleteIntent(action(ACTION_STOP)) + .setOngoing(playing) + .setShowWhen(false) + .setVisibility(Notification.VISIBILITY_PUBLIC) + .addAction( + Notification.Action.Builder( + Icon.createWithResource(this, SidekitIcon.SkipPrevious.drawable), + "Previous", + action(ACTION_PREVIOUS) + ).build() + ) + .addAction( + Notification.Action.Builder( + Icon.createWithResource( + this, + if (playing) SidekitIcon.Pause.drawable else SidekitIcon.PlayArrow.drawable + ), + if (playing) "Pause" else "Play", + action(ACTION_PLAY_PAUSE) + ).build() + ) + .addAction( + Notification.Action.Builder( + Icon.createWithResource(this, SidekitIcon.SkipNext.drawable), + "Next", + action(ACTION_NEXT) + ).build() + ) + .setStyle( + Notification.MediaStyle() + .setMediaSession(playback.sessionToken) + .setShowActionsInCompactView(0, 1, 2) + ) + .build() + } + + private fun action(action: String): PendingIntent = PendingIntent.getForegroundService( + this, + action.hashCode(), + Intent(this, SpotifyPlaybackService::class.java).setAction(action), + PendingIntent.FLAG_IMMUTABLE + ) + + companion object { + private const val ACTION_PLAY_PAUSE = "hrln.interactive.sidekit.sidetracks.PLAY_PAUSE" + private const val ACTION_NEXT = "hrln.interactive.sidekit.sidetracks.NEXT" + private const val ACTION_PREVIOUS = "hrln.interactive.sidekit.sidetracks.PREVIOUS" + private const val ACTION_STOP = "hrln.interactive.sidekit.sidetracks.STOP" + + private var running: SpotifyPlaybackService? = null + + /** Brings the service in line with the player. Main thread only, like the player. */ + fun sync(context: Context, playback: SpotifyPlayback) { + val service = running + if (service != null) { + service.update(playback) + return + } + if (!playback.isActive || playback.current == null) return + try { + context.startForegroundService(Intent(context, SpotifyPlaybackService::class.java)) + } catch (_: IllegalStateException) { + // Not allowed from the background. Playback still works, just without the + // foreground-service protection on this edge. + } + } + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt new file mode 100644 index 00000000..4f854e78 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt @@ -0,0 +1,117 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative +import hrln.interactive.sidekit.apps.spotify.data.NativeSpotifyCatalog +import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class NativeSpotifyCatalogTest { + @Test fun `track save writes the collection set`() = runTest { + val native = FakeNative() + val catalog = NativeSpotifyCatalog(native) {} + + catalog.setTrackSaved("abc", true) + + assertEquals("collection", native.set) + assertEquals("[\"spotify:track:abc\"]", native.uris) + assertFalse(native.removed) + } + + @Test fun `album and show saves use their collection sets`() = runTest { + val native = FakeNative() + val catalog = NativeSpotifyCatalog(native) {} + + catalog.setSaved(SpotifyCollectionType.Album, "album1", false) + assertEquals("collection", native.set) + assertTrue(native.removed) + + catalog.setSaved(SpotifyCollectionType.Show, "show1", true) + assertEquals("show", native.set) + assertFalse(native.removed) + } + + @Test fun `playlist save uses the rootlist endpoint`() = runTest { + val native = FakeNative() + val catalog = NativeSpotifyCatalog(native) {} + + catalog.setSaved(SpotifyCollectionType.Playlist, "playlist1", true) + + assertEquals("playlist1", native.playlistId) + assertTrue(native.playlistSave) + } + + @Test fun `contains responses seed saved state`() = runTest { + val native = FakeNative().apply { + contains = "[true]" + playlistSaved = "true" + } + val catalog = NativeSpotifyCatalog(native) {} + + assertTrue(catalog.isTrackSaved("abc") == true) + assertTrue(catalog.isSaved(SpotifyCollectionType.Playlist, "playlist1") == true) + } + + @Test fun `failed write surfaces a network failure`() = runTest { + val native = FakeNative().apply { writeOk = false } + val catalog = NativeSpotifyCatalog(native) {} + + val error = runCatching { catalog.setTrackSaved("abc", true) }.exceptionOrNull() + + assertTrue(error is SpotifyGatewayException) + assertEquals(SpotifyFailure.Network, (error as SpotifyGatewayException).failure) + } + + private class FakeNative : SpotifyNative { + override val isAvailable = true + var set: String? = null + var uris: String? = null + var removed = false + var writeOk = true + var contains = "[false]" + var playlistSaved = "false" + var playlistId: String? = null + var playlistSave = false + + override fun initialize() = true + override fun setTmpDir(path: String) = Unit + override fun connect(accessToken: String) = true + override fun setAccessToken(accessToken: String) = true + override fun disconnect() = Unit + override fun createPlayer(bitrateKbps: Int) = true + override fun load(uri: String, positionMs: Long, autoplay: Boolean) = true + override fun play() = true + override fun pause() = true + override fun stop() = true + override fun seek(positionMs: Long) = true + override fun readAudio(timeoutMs: Long): ByteArray? = null + override fun audioFormat(): String? = null + override fun pollEvent(): String? = null + override fun catalogLibrary(filter: String): String? = null + override fun catalogCollection(kind: String, id: String): String? = null + override fun catalogArtist(id: String): String? = null + override fun catalogAlbumsBy(id: String): String? = null + override fun catalogTopTracks(id: String): String? = null + override fun catalogSearch(query: String, scopes: String, limit: Long): String? = null + override fun collectionContains(set: String, uris: String): String? = contains + override fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean { + this.set = set + this.uris = uris + this.removed = removed + return writeOk + } + override fun playlistSaved(id: String): String? = playlistSaved + override fun playlistSave(id: String, save: Boolean): Boolean { + playlistId = id + playlistSave = save + return writeOk + } + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt index 525df70a..82b3ecbf 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -4,6 +4,7 @@ import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative import hrln.interactive.sidekit.apps.spotify.data.NativeSpotifyGateway import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException +import hrln.interactive.sidekit.apps.spotify.data.SpotifySettings import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -47,24 +48,67 @@ class NativeSpotifyGatewayTest { assertEquals("stored", native.token) } + @Test fun `player receives persisted bitrate`() = runTest { + val native = FakeNative() + val gateway = NativeSpotifyGateway( + native, + { SpotifySettings(audioQuality = hrln.interactive.sidekit.apps.spotify.AudioQuality.Normal) } + ) { "token" } + + gateway.connect("token") + + assertEquals(96, native.bitrateKbps) + } + + @Test fun `rotated token is pushed into the live session`() = runTest { + val native = FakeNative() + var token = "first" + val gateway = NativeSpotifyGateway(native) { token } + gateway.connect(token) + token = "second" + + gateway.ensureConnected() + + assertEquals("second", native.token) + assertEquals(1, native.connects) + } + + @Test fun `dead native session reconnects from scratch`() = runTest { + val native = FakeNative().apply { setTokenOk = false } + var token = "first" + val gateway = NativeSpotifyGateway(native) { token } + gateway.connect(token) + token = "second" + + gateway.ensureConnected() + + assertEquals("second", native.token) + assertEquals(2, native.connects) + } + private class FakeNative(override val isAvailable: Boolean = true) : SpotifyNative { var token: String? = null var playerCreated = false var loadOk = true + var setTokenOk = true + var bitrateKbps: Int? = null + var connects = 0 override fun initialize() = isAvailable override fun setTmpDir(path: String) = Unit override fun connect(accessToken: String): Boolean { + connects++ token = accessToken return isAvailable } override fun setAccessToken(accessToken: String): Boolean { - token = accessToken - return isAvailable + if (setTokenOk) token = accessToken + return setTokenOk && isAvailable } override fun disconnect() = Unit - override fun createPlayer(): Boolean { + override fun createPlayer(bitrateKbps: Int): Boolean { playerCreated = true + this.bitrateKbps = bitrateKbps return isAvailable } override fun load(uri: String, positionMs: Long, autoplay: Boolean) = loadOk @@ -72,7 +116,6 @@ class NativeSpotifyGatewayTest { override fun pause() = isAvailable override fun stop() = isAvailable override fun seek(positionMs: Long) = isAvailable - override fun setVolume(volume: Float) = Unit override fun readAudio(timeoutMs: Long): ByteArray? = null override fun audioFormat(): String? = null override fun pollEvent(): String? = null @@ -82,5 +125,9 @@ class NativeSpotifyGatewayTest { override fun catalogAlbumsBy(id: String): String? = null override fun catalogTopTracks(id: String): String? = null override fun catalogSearch(query: String, scopes: String, limit: Long): String? = null + override fun collectionContains(set: String, uris: String): String? = null + override fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean = true + override fun playlistSaved(id: String): String? = null + override fun playlistSave(id: String, save: Boolean): Boolean = true } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt index ccbaf71b..f9ccd435 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -46,6 +46,8 @@ class SidetracksScreenshotTest { onSettings = {}, onRefresh = {}, onPlay = {}, + onQueue = {}, + onQueueTrack = {}, onDismissNotice = {} ) } @@ -57,6 +59,7 @@ class SidetracksScreenshotTest { onSearch = {}, onFilter = {}, onTrack = {}, + onQueue = {}, onPlaylist = {}, onAlbum = {}, onArtist = {}, @@ -75,7 +78,9 @@ class SidetracksScreenshotTest { onSave = {}, onArtist = {}, onQueue = {}, + onQueueItem = {}, onNowPlaying = {}, + onUndo = {}, onDismissNotice = {} ) } @@ -90,9 +95,11 @@ class SidetracksScreenshotTest { onBack = {}, onPlay = {}, onTrack = {}, + onQueue = {}, onAlbum = {}, onFollow = {}, onNowPlaying = {}, + onUndo = {}, onDismissNotice = {} ) } @@ -105,6 +112,7 @@ class SidetracksScreenshotTest { onClear = {}, onToggleScope = {}, onTrack = {}, + onQueue = {}, onArtist = {}, onAlbum = {}, onPlaylist = {}, @@ -127,6 +135,7 @@ class SidetracksScreenshotTest { onQueue = {}, onAlbum = {}, onArtist = {}, + onUndo = {}, onDismissNotice = {} ) } @@ -151,7 +160,6 @@ class SidetracksScreenshotTest { state, onBack = {}, onAudioQuality = {}, - onVolume = {}, onClearCache = {}, onClearHistory = {}, onSignOut = {}, diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt index a17f4c87..c5fbe165 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt @@ -99,7 +99,6 @@ class SpotifyPlaybackTest { } override suspend fun seek(positionMs: Long) = Unit - override suspend fun setVolume(volume: Float) = Unit } private class FakeOwner : MediaSessionOwner { diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt index 2b0162cd..b0bb42d2 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt @@ -42,6 +42,16 @@ class SpotifyQueueRepositoryTest { assertEquals(listOf("c", "b", "d"), queue.snapshot.upcoming.map { it.id }) } + @Test fun `restore adopts a process-wide snapshot`() { + val queue = SpotifyQueueRepository(Random(1)) + val snapshot = queue.play(tracks, 1) + + val restored = SpotifyQueueRepository(Random(2)).restore(snapshot) + + assertEquals("b", restored.current?.id) + assertEquals(listOf("c", "d"), restored.upcoming.map { it.id }) + } + @Test fun `add to queue appends`() { val queue = SpotifyQueueRepository(Random(1)) queue.play(tracks, 0) diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs index 7a405ee8..2199b400 100644 --- a/native/sidekit-spotify/src/catalog.rs +++ b/native/sidekit-spotify/src/catalog.rs @@ -20,10 +20,13 @@ use librespot_protocol::extended_metadata::{ BatchedEntityRequest, BatchedExtensionResponse, EntityRequest, ExtensionQuery, }; use librespot_protocol::extension_kind::ExtensionKind; -use librespot_protocol::playlist4_external::SelectedListContent; -use protobuf::{EnumOrUnknown, Message}; +use librespot_protocol::playlist4_external::{ + Add, Delta, Item as PlaylistItem, ListChanges, Op, Rem, SelectedListContent, +}; +use protobuf::{EnumOrUnknown, Message, MessageField}; use serde::Serialize; use serde_json::{json, Value}; +use std::time::{SystemTime, UNIX_EPOCH}; const PATHFINDER_BASE: &str = "https://api-partner.spotify.com"; const SEARCH_QUERY_HASH: &str = "d9f785900f0710b31c07818d617f4f7600c1e21217e80f5b043d1e78d74e6026"; @@ -1106,6 +1109,202 @@ async fn rootlist_playlists( Ok(playlists) } + +fn collection_item_bytes(uri: &str, added_at: i32, removed: bool) -> Vec { + let mut item = Vec::new(); + put_string(&mut item, 1, uri); + if added_at > 0 { + put_varint(&mut item, (2 << 3) as u64); + put_varint(&mut item, added_at as u64); + } + if removed { + put_varint(&mut item, (3 << 3) as u64); + put_varint(&mut item, 1); + } + item +} + +fn collection_body(username: &str, set: &str, uris: &[String], removed: bool) -> Vec { + let mut body = Vec::new(); + put_string(&mut body, 1, username); + put_string(&mut body, 2, set); + let added_at = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_secs() as i32) + .unwrap_or(0); + for uri in uris { + let item = collection_item_bytes(uri, if removed { 0 } else { added_at }, removed); + put_varint(&mut body, ((3 << 3) | 2) as u64); + put_varint(&mut body, item.len() as u64); + body.extend_from_slice(&item); + } + body +} + +fn parse_contains(bytes: &[u8]) -> Vec { + let mut found = Vec::new(); + let mut index = 0; + while index < bytes.len() { + let Some(key) = read_varint(bytes, &mut index) else { + break; + }; + let field = (key >> 3) as u32; + let wire = (key & 7) as u8; + match (field, wire) { + (1, 0) => { + let Some(value) = read_varint(bytes, &mut index) else { + break; + }; + found.push(value != 0); + } + (_, 0) => { + let _ = read_varint(bytes, &mut index); + } + (_, 1) => index += 8, + (_, 2) => { + let Some(len) = read_varint(bytes, &mut index) else { + break; + }; + index += len as usize; + } + (_, 5) => index += 4, + _ => break, + } + } + found +} + +/// Answers in the order of `uris`; used to seed the optimistic save state before a toggle. +pub async fn collection_contains( + session: &Session, + token: &str, + set: &str, + uris: &[String], +) -> Result { + let body = collection_body(&session.username(), set, uris, false); + let bytes = sp_request( + session, + token, + Method::POST, + "/collection/v2/contains", + Some("application/vnd.collection-v2.spotify.proto"), + &body, + ) + .await?; + let found = parse_contains(&bytes); + if found.len() != uris.len() { + return Err("collection contains returned the wrong number of results".to_string()); + } + serde_json::to_string(&found).map_err(|e| e.to_string()) +} + +/// Adds or removes items from a collection set. `is_removed` marks a removal. +pub async fn collection_write( + session: &Session, + token: &str, + set: &str, + uris: &[String], + removed: bool, +) -> Result<(), String> { + let body = collection_body(&session.username(), set, uris, removed); + sp_request( + session, + token, + Method::POST, + "/collection/v2/write", + Some("application/vnd.collection-v2.spotify.proto"), + &body, + ) + .await?; + Ok(()) +} + +async fn rootlist_content(session: &Session, token: &str) -> Result { + let user = session.username(); + let endpoint = format!( + "/playlist/v2/user/{user}/rootlist?decorate=revision,attributes,length,owner,capabilities,status_code&from=0&length=500" + ); + let bytes = sp_request(session, token, Method::GET, &endpoint, None, &[]) + .await + .map_err(|e| format!("rootlist: {e}"))?; + SelectedListContent::parse_from_bytes(&bytes).map_err(|e| e.to_string()) +} + +pub async fn playlist_saved(session: &Session, token: &str, id: &str) -> Result { + let uri = uri_of(id, "playlist"); + let content = rootlist_content(session, token).await?; + Ok(content + .contents + .as_ref() + .is_some_and(|items| items.items.iter().any(|item| item.uri() == uri))) +} + +/// Adds or removes a playlist from the user's rootlist (the "Your Library" playlist list). +pub async fn playlist_save( + session: &Session, + token: &str, + id: &str, + save: bool, +) -> Result<(), String> { + let uri = uri_of(id, "playlist"); + let content = rootlist_content(session, token).await?; + let index = content + .contents + .as_ref() + .and_then(|items| items.items.iter().position(|item| item.uri() == uri)); + if save == index.is_some() { + return Ok(()); + } + + let mut changes = ListChanges::new(); + changes.base_revision = content.revision.clone(); + let mut delta = Delta::new(); + if save { + let item = PlaylistItem { + uri: Some(uri), + ..Default::default() + }; + let add = Add { + items: vec![item], + add_last: Some(true), + ..Default::default() + }; + delta.ops.push(Op { + kind: Some(EnumOrUnknown::new( + librespot_protocol::playlist4_external::op::Kind::ADD, + )), + add: MessageField::some(add), + ..Default::default() + }); + } else { + let rem = Rem { + from_index: Some(index.unwrap_or(0) as i32), + length: Some(1), + ..Default::default() + }; + delta.ops.push(Op { + kind: Some(EnumOrUnknown::new( + librespot_protocol::playlist4_external::op::Kind::REM, + )), + rem: MessageField::some(rem), + ..Default::default() + }); + } + changes.deltas.push(delta); + let body = changes.write_to_bytes().map_err(|e| e.to_string())?; + let user = session.username(); + sp_request( + session, + token, + Method::POST, + &format!("/playlist/v2/user/{user}/rootlist/changes"), + Some("application/x-www-form-urlencoded"), + &body, + ) + .await?; + Ok(()) +} + pub async fn library(session: &Session, token: &str, filter: &str) -> Result { match filter { "LikedSongs" => { diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index 22a679ec..9d24f333 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -161,7 +161,7 @@ pub fn disconnect() { } } -pub fn create_player() -> Result<(), String> { +pub fn create_player(bitrate_kbps: u16) -> Result<(), String> { let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; if guard.player.is_some() { return Ok(()); @@ -177,8 +177,13 @@ pub fn create_player() -> Result<(), String> { .ok_or_else(|| "AUDIO_UNINITIALISED".to_string())? .clone(); let volume = guard.volume.clone(); + let bitrate = match bitrate_kbps { + 0..=96 => Bitrate::Bitrate96, + 97..=160 => Bitrate::Bitrate160, + _ => Bitrate::Bitrate320, + }; let config = PlayerConfig { - bitrate: Bitrate::Bitrate320, + bitrate, position_update_interval: Some(Duration::from_millis(500)), ..PlayerConfig::default() }; @@ -226,13 +231,6 @@ pub fn seek(position_ms: u32) -> Result<(), String> { Ok(()) } -pub fn set_volume(volume: f32) { - let level = (volume.clamp(0.0, 1.0) * 65_535.0) as u16; - if let Ok(guard) = state().lock() { - guard.volume.0.store(level, Ordering::Relaxed); - } -} - pub fn read_audio(timeout: Duration) -> Option> { audio_rx().lock().ok()?.recv_timeout(timeout).ok() } diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index 36a29420..2213ee39 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -5,7 +5,7 @@ mod catalog; mod engine; use jni::objects::{JObject, JString}; -use jni::sys::{jboolean, jbyteArray, jfloat, jlong, JNI_FALSE, JNI_TRUE}; +use jni::sys::{jboolean, jbyteArray, jlong, JNI_FALSE, JNI_TRUE}; use jni::JNIEnv; use std::time::Duration; @@ -86,8 +86,9 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCreatePlayer( _env: JNIEnv, _this: JObject, + bitrate_kbps: jlong, ) -> jboolean { - if engine::create_player().is_ok() { + if engine::create_player(bitrate_kbps.clamp(0, u16::MAX as i64) as u16).is_ok() { JNI_TRUE } else { JNI_FALSE @@ -143,15 +144,6 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB bool_result(engine::seek(position_ms.max(0) as u32)) } -#[no_mangle] -pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSetVolume( - _env: JNIEnv, - _this: JObject, - volume: jfloat, -) { - engine::set_volume(volume); -} - #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeReadAudio( env: JNIEnv, @@ -293,6 +285,85 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB catalog_result(&mut env, result) } + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCollectionContains( + mut env: JNIEnv, + _this: JObject, + set: JString, + uris: JString, +) -> jni::sys::jstring { + let (Some(set), Some(uris)) = (string(&mut env, &set), string(&mut env, &uris)) else { + return std::ptr::null_mut(); + }; + let uris: Vec = serde_json::from_str(&uris).unwrap_or_default(); + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::collection_contains(session, token, &set, &uris)) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCollectionWrite( + mut env: JNIEnv, + _this: JObject, + set: JString, + uris: JString, + removed: jboolean, +) -> jboolean { + let (Some(set), Some(uris)) = (string(&mut env, &set), string(&mut env, &uris)) else { + return JNI_FALSE; + }; + let uris: Vec = serde_json::from_str(&uris).unwrap_or_default(); + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::collection_write( + session, + token, + &set, + &uris, + removed == JNI_TRUE, + )) + }); + bool_result(result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativePlaylistSaved( + mut env: JNIEnv, + _this: JObject, + id: JString, +) -> jni::sys::jstring { + let Some(id) = string(&mut env, &id) else { + return std::ptr::null_mut(); + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::playlist_saved(session, token, &id)) + .and_then(|saved| serde_json::to_string(&saved).map_err(|e| e.to_string())) + }); + catalog_result(&mut env, result) +} + +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativePlaylistSave( + mut env: JNIEnv, + _this: JObject, + id: JString, + save: jboolean, +) -> jboolean { + let Some(id) = string(&mut env, &id) else { + return JNI_FALSE; + }; + let result = engine::with_session_and_token(|session, token| { + engine::run(catalog::playlist_save( + session, + token, + &id, + save == JNI_TRUE, + )) + }); + bool_result(result) +} + fn bool_result(result: Result<(), String>) -> jboolean { if result.is_ok() { JNI_TRUE diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt index 31a023f1..5c044bac 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.graphicsLayer @@ -52,6 +53,16 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlin.math.min +/** The action revealed by a left swipe. Delete is the default; other actions pass their own. */ +data class SwipeAction( + val label: String, + val icon: SidekitIcon, + val color: Color, + val armedColor: Color = color, + val destructive: Boolean = false, + val onClick: () -> Unit +) + /** Keeps at most one row of a list swiped open, like iOS. */ @Stable class SwipeRevealGroup { @@ -72,11 +83,12 @@ private class SwipeMetrics { } /** - * A list row that swipes left to reveal a delete button (iOS style). + * A list row that swipes left to reveal an action button (iOS style). Delete is the default; + * pass [action] for a non-destructive action such as Add to queue. * - * - Swipe past half the button and release to leave it open; tap it to delete. + * - Swipe past half the button and release to leave it open; tap it to run the action. * - Keep dragging past [FULL_SWIPE_FRACTION] of the row and the button stretches to fill it - * (with a haptic tick); release there to delete without the extra tap. + * (with a haptic tick); release there to run the action without the extra tap. * - Tapping the row while open closes it instead of opening the item. * - Opening one row closes any other row in [group]. * @@ -88,9 +100,10 @@ private class SwipeMetrics { fun SidekitSwipeRow( key: Any, group: SwipeRevealGroup, - onDelete: () -> Unit, + onDelete: () -> Unit = {}, modifier: Modifier = Modifier, deleteLabel: String = "Delete", + action: SwipeAction? = null, content: @Composable () -> Unit, ) { val density = LocalDensity.current @@ -99,6 +112,14 @@ fun SidekitSwipeRow( val view = LocalView.current val scope = rememberCoroutineScope() + val swipeAction = action ?: SwipeAction( + label = deleteLabel, + icon = SidekitIcon.DeleteForever, + color = colors.dangerA1, + armedColor = colors.dangerA2, + destructive = true, + onClick = onDelete + ) val buttonPx = with(density) { spacing.itemHeight.toPx() } val gapPx = with(density) { spacing.itemOuterGap.toPx() } val radiusPx = with(density) { 36.dp.toPx() } @@ -122,9 +143,9 @@ fun SidekitSwipeRow( } } - fun delete() { + fun performAction() { group.closeAll() - onDelete() + swipeAction.onClick() } LaunchedEffect(armed) { @@ -149,7 +170,7 @@ fun SidekitSwipeRow( } } - val trash = rememberVectorPainter(ImageVector.vectorResource(SidekitIcon.DeleteForever.drawable)) + val actionIcon = rememberVectorPainter(ImageVector.vectorResource(swipeAction.icon.drawable)) val tint = remember(colors.light) { ColorFilter.tint(colors.light) } Box( @@ -162,7 +183,7 @@ fun SidekitSwipeRow( val left = size.width - revealed val r = min(radiusPx, min(revealed, size.height) / 2f) drawRoundRect( - color = lerp(colors.dangerA1, colors.dangerA2, a), + color = lerp(swipeAction.color, swipeAction.armedColor, a), topLeft = Offset(left, 0f), size = Size(revealed, size.height), cornerRadius = CornerRadius(r, r), @@ -173,7 +194,7 @@ fun SidekitSwipeRow( val iconAlpha = ((revealed - iconPx * 0.5f) / iconPx).coerceIn(0f, 1f) val s = iconPx * lerp(0.6f, 1f, iconAlpha) * (1f + 0.15f * a) translate(cx - s / 2f, (size.height - s) / 2f) { - with(trash) { draw(Size(s, s), alpha = iconAlpha, colorFilter = tint) } + with(actionIcon) { draw(Size(s, s), alpha = iconAlpha, colorFilter = tint) } } } .draggable( @@ -188,7 +209,7 @@ fun SidekitSwipeRow( dragging = false val width = metrics.width.toFloat() when { - armed -> settle(-width, velocity) { delete() } + armed -> settle(-width, velocity) { performAction() } velocity < -FLING_VELOCITY || (offset < openOffset / 2f && velocity < FLING_VELOCITY) -> settle(openOffset, velocity) else -> { @@ -199,7 +220,7 @@ fun SidekitSwipeRow( }, ) .semantics { - customActions = listOf(CustomAccessibilityAction(deleteLabel) { delete(); true }) + customActions = listOf(CustomAccessibilityAction(swipeAction.label) { performAction(); true }) }, ) { Box(Modifier.graphicsLayer { translationX = offset }) { content() } @@ -218,9 +239,11 @@ fun SidekitSwipeRow( Modifier .width(spacing.itemHeight) .fillMaxHeight() - .clickable(role = Role.Button, onClickLabel = deleteLabel, indication = null, interactionSource = null) { - view.performHapticFeedback(HapticFeedbackConstants.REJECT) - delete() + .clickable(role = Role.Button, onClickLabel = swipeAction.label, indication = null, interactionSource = null) { + view.performHapticFeedback( + if (swipeAction.destructive) HapticFeedbackConstants.REJECT else HapticFeedbackConstants.CONTEXT_CLICK + ) + performAction() }, ) } diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt index 7e5beb63..0f5e68cd 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt @@ -22,6 +22,7 @@ enum class SidekitIcon(@DrawableRes val drawable: Int) { Bedtime(R.drawable.sk_ic_bedtime), Bluetooth(R.drawable.sk_ic_bluetooth), Bookmark(R.drawable.sk_ic_bookmark), + BookmarkBorder(R.drawable.sk_ic_bookmark_border), BrandAwareness(R.drawable.sk_ic_brand_awareness), BusMapPin(R.drawable.sk_ic_bus_map_pin), Calculate(R.drawable.sk_ic_calculate), diff --git a/sidekit/src/main/res/drawable/sk_ic_bookmark_border.xml b/sidekit/src/main/res/drawable/sk_ic_bookmark_border.xml new file mode 100644 index 00000000..3a55e50e --- /dev/null +++ b/sidekit/src/main/res/drawable/sk_ic_bookmark_border.xml @@ -0,0 +1,7 @@ + + + + + From 5366feba6502f6f5df69cc03e31cab20d0adda66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 11:49:41 +0100 Subject: [PATCH 21/46] fix(sidetracks): restore shared design APIs and document merge follow-ups --- app/screenshots/sidetracks_album.png | Bin 42077 -> 41356 bytes app/screenshots/sidetracks_library_albums.png | Bin 53676 -> 52732 bytes .../apps/spotify/SpotifyNowPlayingScreens.kt | 1 - .../sidekit/navigation/SidekitNavHost.kt | 10 +- .../interactive/sidekit/navigation/SubApp.kt | 2 +- .../hrln/interactive/sidekit/ui/UndoWindow.kt | 128 ++++++++++++++++++ .../interactive/sidekit/ui/UndoWindowTest.kt | 100 ++++++++++++++ .../sidekit/design/action/SideAction.kt | 64 +++++++-- .../sidekit/design/action/UndoBar.kt | 15 ++ .../sidekit/design/components/Item.kt | 46 +++++-- .../sidekit/design/components/SwipeReveal.kt | 43 ++++-- .../sidekit/design/icons/SidekitIcon.kt | 2 +- .../sidekit/design/theme/SidekitColors.kt | 22 ++- 13 files changed, 377 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/ui/UndoWindow.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/ui/UndoWindowTest.kt create mode 100644 sidekit/src/main/java/hrln/interactive/sidekit/design/action/UndoBar.kt diff --git a/app/screenshots/sidetracks_album.png b/app/screenshots/sidetracks_album.png index 5cc8097350b7a00d0eefa4928282f38217e7c149..fd44f15b6c763893171bb9a97878438b87015ae6 100644 GIT binary patch literal 41356 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 DUVzwb literal 42077 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 ZuRl(UFIm(1DfqZS26{#}K3}tc@ju)w1)%@{ diff --git a/app/screenshots/sidetracks_library_albums.png b/app/screenshots/sidetracks_library_albums.png index 1bd58c714423e3582b40a8b8a6ef533fcbc8099d..db1715fb7e8e49c8cefefd7fbd658154449cacd2 100644 GIT binary patch literal 52732 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 z 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 z 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%0DbpJKlBD!kslg})!W+IXPy$FE>8vOrm#1( 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 bqzh*7gh6F%F2Ioh__?F5uU4UA6ZU@r1HX_2 diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 3e448dc2..8092002d 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -111,7 +111,6 @@ fun SpotifyNowPlayingScreen( Modifier.fillMaxWidth(), style = SidekitTheme.typography.headline, textAlign = TextAlign.Center, - minLines = 1, maxLines = 2 ) if (current.creator.isNotBlank()) { diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt index c81c4877..3fb75cf9 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt @@ -9,12 +9,11 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import hrln.interactive.sidekit.apps.apps.appsGraph -import hrln.interactive.sidekit.apps.vault.vaultGraph import hrln.interactive.sidekit.apps.banking.bankingGraph import hrln.interactive.sidekit.apps.calculator.calculatorGraph import hrln.interactive.sidekit.apps.calendar.calendarGraph -import hrln.interactive.sidekit.apps.clock.clockGraph import hrln.interactive.sidekit.apps.camera.cameraGraph +import hrln.interactive.sidekit.apps.clock.clockGraph import hrln.interactive.sidekit.apps.compass.compassGraph import hrln.interactive.sidekit.apps.files.filesGraph import hrln.interactive.sidekit.apps.health.healthGraph @@ -32,10 +31,11 @@ import hrln.interactive.sidekit.apps.sideport.SideportLinks import hrln.interactive.sidekit.apps.sideport.openPorted import hrln.interactive.sidekit.apps.sideport.sideportGraph import hrln.interactive.sidekit.apps.sideticks.sideticksGraph -import hrln.interactive.sidekit.apps.spotify.sidetracksGraph import hrln.interactive.sidekit.apps.sideweather.sideweatherGraph +import hrln.interactive.sidekit.apps.spotify.sidetracksGraph import hrln.interactive.sidekit.apps.translate.translateGraph import hrln.interactive.sidekit.apps.tripplanning.tripPlanningGraph +import hrln.interactive.sidekit.apps.vault.vaultGraph import hrln.interactive.sidekit.apps.video.videoGraph import hrln.interactive.sidekit.apps.webbrowser.webBrowserGraph import hrln.interactive.sidekit.launcher.LauncherRoute @@ -66,14 +66,14 @@ fun SidekitNavHost(navController: NavHostController = rememberNavController(), h enterTransition = { EnterTransition.None }, exitTransition = { ExitTransition.None }, popEnterTransition = { EnterTransition.None }, - popExitTransition = { ExitTransition.None }, + popExitTransition = { ExitTransition.None } ) { composable(LAUNCHER_ROUTE) { val context = androidx.compose.ui.platform.LocalContext.current LauncherRoute( onOpenApp = { app -> navController.navigate(app.route) { launchSingleTop = true } }, // Ported apps open straight into their projection; if that can't start, Sideport explains why. - onOpenPorted = { app -> if (openPorted(context, app) != null) navController.navigate(SubApp.Sideport.route) { launchSingleTop = true } }, + onOpenPorted = { app -> if (openPorted(context, app) != null) navController.navigate(SubApp.Sideport.route) { launchSingleTop = true } } ) } // Core SideKit apps diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt index 91253b94..3a19b97a 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt @@ -35,7 +35,7 @@ enum class SubApp(val label: String, val icon: SidekitIcon, val designed: Boolea Calendar("Calendar", SidekitIcon.CalendarMonth), Compass("Compass", SidekitIcon.Explore), Health("Health", SidekitIcon.EcgHeart), - Sideport("Sideport", SidekitIcon.DeployedCode), + Sideport("Sideport", SidekitIcon.DeployedCode) ; /** Root navigation route of the app. Nested screens use "$route/...". */ diff --git a/app/src/main/java/hrln/interactive/sidekit/ui/UndoWindow.kt b/app/src/main/java/hrln/interactive/sidekit/ui/UndoWindow.kt new file mode 100644 index 00000000..01ca3c69 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/ui/UndoWindow.kt @@ -0,0 +1,128 @@ +package hrln.interactive.sidekit.ui + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +/** How long a deletion can be taken back, counted only while its screen is visible. */ +const val UNDO_WINDOW_MS = 5_000L + +/** + * One undo offer, measured in visible time. + * + * The five seconds are a budget of attention, not of wall clock: [pause] when the screen leaves + * the foreground and [resume] when it comes back, so leaving Sidekit mid-window does not silently + * burn the offer, and returning shows the time that is actually left. Leaving the screen for good + * is a commit, not a pause: cancel the window and finalise the deletion ([cancel]). + */ +class UndoWindow( + private val scope: CoroutineScope, + private val durationMs: Long = UNDO_WINDOW_MS, + // nanoTime is monotonic on both Android and the JVM, so plain unit tests need no Android stub. + private val elapsed: () -> Long = { System.nanoTime() / 1_000_000L }, + private val onExpire: () -> Unit +) { + private var job: Job? = null + private var remainingMs = durationMs + private var deadline = 0L + private var running = false + private var armed = false + + /** True while a deletion can still be taken back (running or paused). */ + val isPending: Boolean get() = armed + + /** Offers a new deletion, replacing any earlier offer and restarting the five seconds. */ + fun start() { + job?.cancel() + armed = true + remainingMs = durationMs + run() + } + + /** Stops the clock while the screen is not visible; [resume] continues with what is left. */ + fun pause() { + if (!running) return + remainingMs = (deadline - elapsed()).coerceAtLeast(0L) + job?.cancel() + job = null + running = false + } + + /** Continues a paused offer, or expires it if its time ran out at the moment it was paused. */ + fun resume() { + if (!armed || running) return + if (remainingMs <= 0L) expire() else run() + } + + /** Drops the offer without restoring anything. Callers finalise the deletion first. */ + fun cancel() { + job?.cancel() + job = null + running = false + armed = false + remainingMs = durationMs + } + + private fun run() { + running = true + deadline = elapsed() + remainingMs + val wait = remainingMs + job = scope.launch { + delay(wait) + job = null + expire() + } + } + + private fun expire() { + running = false + armed = false + remainingMs = 0L + onExpire() + } +} + +/** + * Binds an [UndoWindow] to the screen that owns it. + * + * Backgrounding pauses the clock and returning resumes it; disposing the screen commits, so the + * offer never follows the user to another conversation, tab or app. + */ +@Composable +fun UndoWindowLifecycle(window: UndoWindow, commit: () -> Unit) { + val lifecycle = LocalLifecycleOwner.current.lifecycle + val latestCommit by rememberUpdatedState(commit) + DisposableEffect(lifecycle, window) { + val observer = LifecycleEventObserver { _, event -> + when (event) { + Lifecycle.Event.ON_STOP -> window.pause() + Lifecycle.Event.ON_START -> window.resume() + else -> Unit + } + } + lifecycle.addObserver(observer) + onDispose { + lifecycle.removeObserver(observer) + window.cancel() + latestCommit() + } + } +} + +/** A window for screens that keep their pending undo in composition state rather than a ViewModel. */ +@Composable +fun rememberUndoWindow(onExpire: () -> Unit): UndoWindow { + val scope = rememberCoroutineScope() + val latestExpire by rememberUpdatedState(onExpire) + return remember { UndoWindow(scope) { latestExpire() } } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/ui/UndoWindowTest.kt b/app/src/test/java/hrln/interactive/sidekit/ui/UndoWindowTest.kt new file mode 100644 index 00000000..051b775c --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/ui/UndoWindowTest.kt @@ -0,0 +1,100 @@ +package hrln.interactive.sidekit.ui + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +@OptIn(ExperimentalCoroutinesApi::class) +class UndoWindowTest { + @Test + fun `expires after five seconds of visible time`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.start() + assertTrue(window.isPending) + advanceTimeBy(4_999) + assertEquals(0, expired) + advanceTimeBy(1) + runCurrent() + assertEquals(1, expired) + assertFalse(window.isPending) + } + + @Test + fun `pausing stops the clock and resuming spends only the time that was left`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.start() + advanceTimeBy(3_000) + window.pause() + advanceTimeBy(60_000) + assertEquals(0, expired) + assertTrue(window.isPending) + window.resume() + advanceTimeBy(1_999) + assertEquals(0, expired) + advanceTimeBy(1) + runCurrent() + assertEquals(1, expired) + } + + @Test + fun `every pause resumes with the same remaining time`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.start() + advanceTimeBy(2_000) + window.pause() + window.resume() + advanceTimeBy(1_000) + window.pause() + advanceTimeBy(10_000) + window.resume() + advanceTimeBy(1_999) + assertEquals(0, expired) + advanceTimeBy(1) + runCurrent() + assertEquals(1, expired) + } + + @Test + fun `cancelling drops the offer without expiring it`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.start() + advanceTimeBy(1_000) + window.cancel() + advanceTimeBy(10_000) + assertEquals(0, expired) + assertFalse(window.isPending) + } + + @Test + fun `a second deletion replaces the first`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.start() + advanceTimeBy(3_000) + window.start() + advanceTimeBy(3_000) + assertEquals(0, expired) + advanceTimeBy(2_000) + runCurrent() + assertEquals(1, expired) + } + + @Test + fun `resume without an offer does nothing`() = runTest { + var expired = 0 + val window = UndoWindow(this, elapsed = { testScheduler.currentTime }) { expired++ } + window.resume() + advanceTimeBy(10_000) + assertEquals(0, expired) + assertFalse(window.isPending) + } +} diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/action/SideAction.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/action/SideAction.kt index 541cfa87..e6ac2ac7 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/action/SideAction.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/action/SideAction.kt @@ -59,7 +59,7 @@ enum class ActionMotion { Up, /** Scales in on the spot. */ - None, + None } /** @@ -91,7 +91,7 @@ class SideAction( val dense: Boolean = false, val artKey: Any? = null, val art: (@Composable () -> Unit)? = null, - val onClick: () -> Unit, + val onClick: () -> Unit ) { init { require(label != null || icon != null || art != null) { "SideAction '$key' needs a label, icon or art" } @@ -99,25 +99,59 @@ class SideAction( } /** What the user can see; a change here plays the content swap. Click handlers are excluded. */ - internal fun sameContent(other: SideAction): Boolean = - label == other.label && icon == other.icon && iconAtEnd == other.iconAtEnd && - dense == other.dense && (art == null) == (other.art == null) && artKey == other.artKey + internal fun sameContent(other: SideAction): Boolean = label == other.label && icon == other.icon && iconAtEnd == other.iconAtEnd && + dense == other.dense && (art == null) == (other.art == null) && artKey == other.artKey /** Everything the bar draws or animates is the same; only click handlers (and art lambdas for the same [artKey]) may differ. */ - internal fun sameLook(other: SideAction): Boolean = - sameContent(other) && tone == other.tone && weight == other.weight && square == other.square && - motion == other.motion && contentDescription == other.contentDescription + internal fun sameLook(other: SideAction): Boolean = sameContent(other) && tone == other.tone && weight == other.weight && square == other.square && + motion == other.motion && contentDescription == other.contentDescription internal val description: String? get() = contentDescription ?: label companion object { /** Wide button that navigates deeper: label then a right arrow. */ - fun forward(key: String, label: String, onClick: () -> Unit, icon: SidekitIcon = SidekitIcon.ArrowRightAlt) = - SideAction(key, label, icon, iconAtEnd = true, motion = ActionMotion.Forward, onClick = onClick) + fun forward(key: String, label: String, onClick: () -> Unit, icon: SidekitIcon = SidekitIcon.ArrowRightAlt) = SideAction(key, label, icon, iconAtEnd = true, motion = ActionMotion.Forward, onClick = onClick) /** Wide button that returns: left arrow then label. */ - fun back(key: String, label: String, onClick: () -> Unit) = - SideAction(key, label, SidekitIcon.ArrowLeftAlt, motion = ActionMotion.Back, onClick = onClick) + fun back(key: String, label: String, onClick: () -> Unit) = SideAction(key, label, SidekitIcon.ArrowLeftAlt, motion = ActionMotion.Back, onClick = onClick) + + /** + * Commit: green, check at the end, rises from below. The screen names the act, the role + * owns the colour — saving looks the same in every sub-app. + */ + fun save(key: String, label: String = "Save", onClick: () -> Unit) = SideAction(key, label, SidekitIcon.Check, iconAtEnd = true, tone = ActionTone.Secondary, motion = ActionMotion.Up, onClick = onClick) + + /** Commit that leaves the screen: green, arrow at the end, enters from the right. */ + fun send(key: String, label: String = "Send", onClick: () -> Unit) = SideAction(key, label, SidekitIcon.ArrowRightAlt, iconAtEnd = true, tone = ActionTone.Secondary, motion = ActionMotion.Forward, onClick = onClick) + + /** + * Take it back: grey, no promise to advance. Cancel and Undo wear the same colour because + * they make the same promise — nothing happened. [icon] and [motion] default to the + * reversing pair (left arrow, enters from the left); a themed confirm passes + * `icon = null, motion = ActionMotion.Up` to stay grey while it dismisses in place. + */ + fun cancel( + key: String = "back", + label: String = "Cancel", + icon: SidekitIcon? = SidekitIcon.ArrowLeftAlt, + motion: ActionMotion = ActionMotion.Back, + contentDescription: String? = null, + onClick: () -> Unit + ) = SideAction(key, label, icon, tone = ActionTone.Muted, motion = motion, contentDescription = contentDescription, onClick = onClick) + + /** + * Take a delete back: grey, rises in place, and describes what it restores so the button + * reads as "Undo delete Barclays" rather than a bare "Undo". + */ + fun undo( + key: String = "undo", + label: String = "Undo", + contentDescription: String? = null, + onClick: () -> Unit + ) = SideAction(key, label, SidekitIcon.Undo, tone = ActionTone.Muted, motion = ActionMotion.Up, contentDescription = contentDescription, onClick = onClick) + + /** Destroy: red, wherever it appears (bar, confirm, Options). [icon] stays optional. */ + fun delete(key: String, label: String = "Delete", icon: SidekitIcon? = null, onClick: () -> Unit) = SideAction(key, label, icon, tone = ActionTone.Destructive, onClick = onClick) /** Square icon-only button. */ fun square( @@ -126,7 +160,7 @@ class SideAction( contentDescription: String, onClick: () -> Unit, tone: ActionTone = ActionTone.Primary, - motion: ActionMotion = ActionMotion.None, + motion: ActionMotion = ActionMotion.None ) = SideAction(key, icon = icon, tone = tone, square = true, motion = motion, contentDescription = contentDescription, onClick = onClick) } } @@ -139,7 +173,7 @@ class SideAction( class HeaderSpec( val title: String, val align: TextAlign = TextAlign.Start, - val modifier: Modifier = Modifier, + val modifier: Modifier = Modifier ) /** @@ -264,7 +298,7 @@ internal class ChromePublisher { opts.map { o -> SideAction( "options.${o.key}", o.label, o.icon, o.iconAtEnd, o.tone, o.weight, o.square, - ActionMotion.Up, o.contentDescription, dense = o.dense || opts.size > 2, artKey = o.artKey, art = o.art, + ActionMotion.Up, o.contentDescription, dense = o.dense || opts.size > 2, artKey = o.artKey, art = o.art ) { close() o.onClick() diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/action/UndoBar.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/action/UndoBar.kt new file mode 100644 index 00000000..c371b68b --- /dev/null +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/action/UndoBar.kt @@ -0,0 +1,15 @@ +package hrln.interactive.sidekit.design.action + +/** + * The bar while a delete can still be taken back. + * + * Undo is an offer, not a mode, so it never removes the thing the screen is for: the bar keeps two + * slots, the left one holds the continuation ([continueWith]) and the right one — which is empty + * until something can be undone — shows [undo] for as long as the window lasts. The undo button is + * only ever added, never substituted, and always lands on the same side. + * + * When a screen has both a way back and a primary action the primary wins the left slot, so the + * exit button steps aside for the window; the system back still leaves, and the exit returns with + * the primary. + */ +fun undoBar(continueWith: SideAction, undo: SideAction? = null): List = if (undo == null) listOf(continueWith) else listOf(continueWith, undo) diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt index f8bdb921..04a04698 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt @@ -13,16 +13,17 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider -import hrln.interactive.sidekit.design.theme.contentColorFor import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.Dp import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme +import hrln.interactive.sidekit.design.theme.contentColorFor /** Item "Colour Variant" from the Item component set (62:950). */ enum class ItemColour { Primary, Secondary, Destructive, Muted } @@ -46,7 +47,7 @@ fun SidekitTile( onClick: (() -> Unit)? = null, onClickLabel: String? = null, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit, + content: @Composable BoxScope.() -> Unit ) { Box( modifier = modifier @@ -54,13 +55,16 @@ fun SidekitTile( .clip(SidekitTheme.shapes.item) .background(color) .then( - if (onClick != null) Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) - else Modifier, + if (onClick != null) { + Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) + } else { + Modifier + } ), contentAlignment = contentAlignment, content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - }, + } ) } @@ -73,31 +77,43 @@ fun SidekitItem( title: String, modifier: Modifier = Modifier, secondary: String? = null, + /** Put the secondary value at the row's trailing edge (toggles and compact state rows). */ + secondaryToEnd: Boolean = false, icon: SidekitIcon? = null, showLinkArrow: Boolean = false, colour: ItemColour = ItemColour.Primary, titleDecoration: TextDecoration? = null, onClick: (() -> Unit)? = null, - leading: (@Composable RowScope.() -> Unit)? = null, + leading: (@Composable RowScope.() -> Unit)? = null ) { SidekitTile( modifier = modifier.fillMaxWidth(), color = colour.container(), onClick = onClick, - contentAlignment = Alignment.CenterStart, + contentAlignment = Alignment.CenterStart ) { Row( modifier = Modifier.padding(horizontal = SidekitTheme.spacing.itemPadding), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap), + horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap) ) { leading?.invoke(this) if (icon != null) SidekitIconImage(icon) SidekitText(title, textDecoration = titleDecoration) if (secondary != null) { - SidekitText(secondary, Modifier.weight(1f), color = SidekitTheme.colors.surfaceA6) - } else if (showLinkArrow) { - Box(Modifier.weight(1f), contentAlignment = Alignment.CenterEnd) { + SidekitText( + secondary, + Modifier.weight(1f), + color = SidekitTheme.colors.surfaceA6, + textAlign = if (secondaryToEnd) TextAlign.End else null + ) + } + // The arrow marks a row that opens another page, whether or not it also shows a value. + if (showLinkArrow) { + Box( + if (secondary == null) Modifier.weight(1f) else Modifier, + contentAlignment = Alignment.CenterEnd + ) { SidekitIconImage(SidekitIcon.ArrowRightAlt) } } @@ -114,7 +130,7 @@ fun SidekitIconItem( colour: ItemColour = ItemColour.Primary, color: Color = colour.container(), iconSize: Dp = SidekitTheme.spacing.iconSize, - onClick: (() -> Unit)? = null, + onClick: (() -> Unit)? = null ) { SidekitTile(modifier = modifier, color = color, onClick = onClick, onClickLabel = contentDescription) { SidekitIconImage(icon, size = iconSize, contentDescription = contentDescription) @@ -127,7 +143,7 @@ fun SidekitItemRow(modifier: Modifier = Modifier, content: @Composable RowScope. Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemOuterGap), - content = content, + content = content ) } @@ -137,7 +153,7 @@ fun SidekitCard( modifier: Modifier = Modifier, color: Color = SidekitTheme.colors.surfaceA3, onClick: (() -> Unit)? = null, - content: @Composable BoxScope.() -> Unit, + content: @Composable BoxScope.() -> Unit ) { Box( modifier = modifier @@ -147,6 +163,6 @@ fun SidekitCard( .padding(SidekitTheme.spacing.itemPadding), content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - }, + } ) } diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt index 5c044bac..05e916fd 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/SwipeReveal.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Size @@ -43,15 +44,14 @@ import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.unit.dp import androidx.compose.ui.util.lerp import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.motion.SidekitMotion import hrln.interactive.sidekit.design.theme.SidekitTheme +import kotlin.math.min import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import kotlin.math.min /** The action revealed by a left swipe. Delete is the default; other actions pass their own. */ data class SwipeAction( @@ -104,7 +104,7 @@ fun SidekitSwipeRow( modifier: Modifier = Modifier, deleteLabel: String = "Delete", action: SwipeAction? = null, - content: @Composable () -> Unit, + content: @Composable () -> Unit ) { val density = LocalDensity.current val colors = SidekitTheme.colors @@ -153,7 +153,11 @@ fun SidekitSwipeRow( arm.animateTo(if (armed) 1f else 0f, SidekitMotion.settle) } LaunchedEffect(isOpen) { - if (isOpen) group.openKey = key else if (group.openKey == key && !dragging) group.openKey = null + if (isOpen) { + group.openKey = key + } else if (group.openKey == key && !dragging) { + group.openKey = null + } } LaunchedEffect(group, key) { snapshotFlow { group.openKey }.collect { open -> if (open != key && !dragging && offset != 0f) settle(0f) } @@ -186,11 +190,14 @@ fun SidekitSwipeRow( color = lerp(swipeAction.color, swipeAction.armedColor, a), topLeft = Offset(left, 0f), size = Size(revealed, size.height), - cornerRadius = CornerRadius(r, r), + cornerRadius = CornerRadius(r, r) ) // Icon sits centred in the button, then jumps to the leading edge once armed. - val cx = if (revealed <= buttonPx) left + revealed / 2f - else lerp(size.width - buttonPx / 2f, left + buttonPx / 2f, a) + val cx = if (revealed <= buttonPx) { + left + revealed / 2f + } else { + lerp(size.width - buttonPx / 2f, left + buttonPx / 2f, a) + } val iconAlpha = ((revealed - iconPx * 0.5f) / iconPx).coerceIn(0f, 1f) val s = iconPx * lerp(0.6f, 1f, iconAlpha) * (1f + 0.15f * a) translate(cx - s / 2f, (size.height - s) / 2f) { @@ -210,18 +217,25 @@ fun SidekitSwipeRow( val width = metrics.width.toFloat() when { armed -> settle(-width, velocity) { performAction() } + velocity < -FLING_VELOCITY || (offset < openOffset / 2f && velocity < FLING_VELOCITY) -> settle(openOffset, velocity) + else -> { settle(0f, velocity) if (group.openKey == key) group.closeAll() } } - }, + } ) .semantics { - customActions = listOf(CustomAccessibilityAction(swipeAction.label) { performAction(); true }) - }, + customActions = listOf( + CustomAccessibilityAction(swipeAction.label) { + performAction() + true + } + ) + } ) { Box(Modifier.graphicsLayer { translationX = offset }) { content() } if (isOpen) { @@ -230,7 +244,12 @@ fun SidekitSwipeRow( Modifier .matchParentSize() .graphicsLayer { translationX = offset } - .pointerInput(Unit) { detectTapGestures { settle(0f); group.closeAll() } }, + .pointerInput(Unit) { + detectTapGestures { + settle(0f) + group.closeAll() + } + } ) // matchParentSize: rows sit in a lazy list (unbounded height), so the tap target takes // the row's measured height rather than fillMaxHeight's (which would be 0). @@ -244,7 +263,7 @@ fun SidekitSwipeRow( if (swipeAction.destructive) HapticFeedbackConstants.REJECT else HapticFeedbackConstants.CONTEXT_CLICK ) performAction() - }, + } ) } } diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt index 0f5e68cd..f325b027 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/icons/SidekitIcon.kt @@ -69,5 +69,5 @@ enum class SidekitIcon(@DrawableRes val drawable: Int) { Undo(R.drawable.sk_ic_undo), Travel(R.drawable.sk_ic_travel), TravelExplore(R.drawable.sk_ic_travel_explore), - Videocam(R.drawable.sk_ic_videocam), + Videocam(R.drawable.sk_ic_videocam) } diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/theme/SidekitColors.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/theme/SidekitColors.kt index eb7e3a67..35f649d5 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/theme/SidekitColors.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/theme/SidekitColors.kt @@ -32,16 +32,26 @@ data class SidekitColors( val surfaceA5: Color = Color(0xFF717171), val surfaceA6: Color = Color(0xFF8B8B8B), val dark: Color = Color(0xFF000000), - val light: Color = Color(0xFFFFFFFF), -) + val light: Color = Color(0xFFFFFFFF) +) { + /** + * Content emphasis: the selected day, a progress fill, a route, your own message, a marker. + * + * Deliberately the same dark green a commit action wears, and deliberately *not* [primaryA1]: + * primary is the case colour and changes with the hardware, so content painted in it would + * change meaning per skin. Emphasis is fixed; only actions and the colour picker may use + * primary. + */ + val emphasisA1: Color get() = successA1 + val emphasisA2: Color get() = successA2 + val emphasisA3: Color get() = successA3 +} /** Luminance above which content on a fill should switch from white to black. */ private const val CONTENT_LUMINANCE = 0.3f /** Black or white, whichever reads against an arbitrary [background] (e.g. a colour swatch). */ -fun readableContentOn(background: Color): Color = - if (background.luminance() > CONTENT_LUMINANCE) Color(0xFF000000) else Color(0xFFFFFFFF) +fun readableContentOn(background: Color): Color = if (background.luminance() > CONTENT_LUMINANCE) Color(0xFF000000) else Color(0xFFFFFFFF) /** Keep labels readable when a light case colour replaces the default blue. */ -fun SidekitColors.contentColorFor(background: Color): Color = - if (background in listOf(primaryA1, primaryA2, primaryA3)) readableContentOn(background) else light +fun SidekitColors.contentColorFor(background: Color): Color = if (background in listOf(primaryA1, primaryA2, primaryA3)) readableContentOn(background) else light From 1f93f2909b6ec534786e15a41b06481c88f25f85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 11:52:13 +0100 Subject: [PATCH 22/46] fix(sidetracks): pin the NDK for the native build --- app/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a2072207..447ec236 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -37,6 +37,7 @@ fun versionCodeFromGit() = runCatching { gitVersionCode.get() }.getOrDefault(fal fun versionNameFromGit() = runCatching { gitVersionName.get() }.getOrDefault(fallbackVersionName) android { + ndkVersion = "27.1.12297006" namespace = "hrln.interactive.sidekit" compileSdk { version = release(37) From 4b786c9f057fd44b61e236e9eed2fe96fa671617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 12:22:37 +0100 Subject: [PATCH 23/46] docs(sidetracks): consolidate into one status and roadmap --- docs/spotify/SIDETRACKS.md | 215 +++++++++++++++++++++++++++++++ native/sidekit-spotify/README.md | 4 +- 2 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 docs/spotify/SIDETRACKS.md diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md new file mode 100644 index 00000000..68c8c7a6 --- /dev/null +++ b/docs/spotify/SIDETRACKS.md @@ -0,0 +1,215 @@ +# Sidetracks + +**Updated:** 2026-11-09 +**Branch:** `feat/sidetracks` +**Phase:** feature-complete for daily use; pre-merge hardening and parity work remain + +Sidetracks is Sidekit's Spotify sub-app: one account's library, search, playback, and queue, +drawn with Sidekit's chrome and operated keypad-first on a 480 × 640 phone. It is not a Spotify +client reproduction and not a web wrapper. + +The implementation has moved past the porting/mockup stage. This document is the single source +of truth for what exists, what must not regress, and what happens next. Older clean-room specs +and status logs were folded into it; the full pre-consolidation text remains in Git history. + +--- + +## 1. Guardrails + +These are constraints, not preferences. A change that violates them is wrong even if it matches +Spotify. + +- **Chrome belongs to the shell.** `SidekitScaffold` draws title, body, and action bar. No app + bottom bar, FAB, tabs, mini-player, sheets, dialogs, toasts, or snackbars. +- **Action bar grammar.** Left = continuation (primary action, else Back), right = next/undo. + Secondary actions live in keypad Options. Destructive actions swap the bar for confirmation. +- **The body is the control surface.** Play, save, and transport are tiles/rows; the bar navigates. +- **One column, explicit pagination.** No carousels, infinite scroll, or algorithmic shelves. + Long lists end with a "Load more" row. +- **Keypad first.** Every interactive row is focusable, has a visible focus ring, and a 48 dp + minimum target. Do not fade the focused element. +- **Content never wears the theme colour.** Selection, progress, and saved state use emphasis; + the case colour belongs to primary actions. +- **No new dependencies** for images, DI, persistence, or media. Reuse `RemoteArtwork`, + `PrivateSettings`, `NativeLibraries`, `MediaSessions`, plain repositories, and manual wiring. +- **No new icons** unless design decides; v1 needed none. +- **Explicit non-goal:** Spotify feature parity. Sidekit parity is the bar. + +Rejected on purpose: algorithmic home feed, horizontal shelves, persistent mini-player, bottom +navigation, full-screen drag-dismiss player, queue bottom sheet, Spotify Connect device switching, +lyrics/Canvas/video, social features, account management, crossfade/EQ, and track-seeded radio. + +--- + +## 2. Shipped + +**Architecture** + +- Playback: `SpotifySession` → `NativeSpotifyGateway` → `native/sidekit-spotify` (librespot 0.8.0) + → `SpotifyAudioSink` (streaming `AudioTrack`). +- Catalog: `NativeSpotifyCatalog` reads through the authenticated librespot session + (SpClient/Pathfinder). `SpotifyWebCatalog` remains a fallback when native is unavailable. +- UI: `SpotifyViewModel` maps catalog responses, queue/history repositories, and native player + events into one immutable `SpotifyUiState`. +- Screens: Home, Library, Collection (album/playlist/show), Artist, Search, Now Playing, Queue, + Settings, Sign in. Mock data is confined to the screenshot-test source set. + +**Behaviour** + +- Library, collection, artist, and grouped search reads use first-party endpoints, avoiding the + public Web API quota and the old HTTP 429 failures. +- Native events (`loading`, `playing`, `paused`, `position`, `ended`, `unavailable`) drive Now + Playing and advance the local queue exactly once. The one-second simulation runs only when + native is absent. +- Library write-back is optimistic with rollback: like/unlike, save/remove album and show, + follow/unfollow artist, add/remove playlist. Saves and follows offer Undo for five visible + seconds; queue actions are silent. +- Playback is process-wide. Queue and local history live on `SpotifySession`; a recreated + ViewModel adopts the live item, position, and playing state. Starting a new item releases the + previous `AudioTrack`. +- Foreground `mediaPlayback` service and platform `MediaSession` keep librespot alive with the + screen off. Notification shows artwork, title, artist, and previous/play-pause/next. +- Sidetracks and local Music pause each other through `MediaSessions`; Sidetracks takes and + abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. +- OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, + releases the media session, removes the notification, then clears credentials. +- Audio quality persists encrypted across process death and maps to 96/160/320 kbps. Playback + volume is the device output volume; there is no separate attenuation. +- Artwork is memory-safe on device: 300 px URL preference, `RGB_565` + `inSampleSize`, 1024 px + cap, `OutOfMemoryError` treated as a cache miss. +- Home's action bar opens the queue; track rows across the app expose Add to queue on left swipe. + Library uses the standard single-line header; Settings has one sign-out action in the bar. + +**Verified** + +- `spotlessCheck`, `:app:lintDebug`, `:app:testDebugUnitTest`, `:app:assembleDebug` pass. +- APK contains `arm64-v8a` and `x86_64` `libsidekit_spotify.so`. +- On `SP01GE260600886` (480 × 640): libraries load real data, search returns Pathfinder tracks, + playback advances/pauses correctly, show episodes format dates, and artwork no longer crashes. + +--- + +## 3. Known limitations + +1. **No recently-played protocol endpoint.** `NativeSpotifyCatalog.recentlyPlayed()` returns + empty; Home falls back to local history. +2. **Local queue ownership.** librespot plays one item at a time; Sidekit drives queue, shuffle, + and repeat. Queue and history survive leaving the app but not process death. +3. **Playlist owner names.** Followed playlists show the owner's Spotify username. +4. **Hard read limits.** Collection 500, playlist tracks 100, show episodes 50, artist albums 50, + search 20. These truncate silently today. +5. **Native `stopped` event is unhandled**, so stopping from the notification can leave a stale + track on screen. +6. **Doc drift.** `native/sidekit-spotify/README.md` claims no librespot patch is applied; one + is (see §6). + +--- + +## 4. Before merge + +Close these before proposing the branch for merge. They are correctness/contract issues, not +parity work. + +1. **Notification transport without a ViewModel.** Play/pause/stop go through `SpotifySession`, + but next/previous route through `SpotifyViewModel`; if it is cleared those buttons do nothing. + Move queue ownership or a fallback transport into the session/playback layer, or disable the + buttons when no queue owner is attached. +2. **Notification tap target.** The content intent opens `MainActivity` at the launcher. Deep-link + it to Sidetracks/Now Playing. +3. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave + stale UI. +4. **Queue/history persistence.** Persist across process death using the existing atomic-file + convention, or state the limitation explicitly in the PR. +5. **Align shipped contracts with docs.** Current implementation is authoritative: session id + `"sidetracks"`, channel `"sidetracks"`, notification id `0x6D76`. Update any remaining + `"spotify"`/`0x5354` references. +6. **Service/audio-focus instrumentation.** Cover the foreground service, media session, + audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and + screenshots only. +7. **Fix the native README patch claim** and link it to §6 below. + +--- + +## 5. What's next + +Ordered by user-visible impact. Each item names its seam so it does not become another +cross-cutting refactor. + +### P0 — device verification for daily use + +- Verify token rotation on a session older than one hour; record the result in the device + checklist. +- Verify library write-back on device: like/unlike, save/remove album and show, follow/unfollow + artist, add/remove playlist; confirm in another Spotify client and after restart. +- Run the existing device checklist for lock screen, notification transport, audio-focus hand-off + with local Music, and headphone unplug. + +### P1 — parity and completeness + +1. **Pagination.** + - Page liked songs, playlists, albums, artists, shows, playlist tracks, show episodes, artist + albums, and search. + - Remove silent truncation or surface "showing N of M". + - Acceptance: a 202-track playlist scrolls to its last track. +2. **Recently played.** + - Choose a supported first-party history endpoint or keep local history only. + - If the Web API is the only source, make it opt-in and degrade to local history when the + quota is exhausted. +3. **Queue/shuffle/repeat ownership.** + - Decide whether librespot player state or `SpotifyQueueRepository` is authoritative. + - If Sidekit stays authoritative, mirror shuffle/repeat/autoplay into the native player and + add an explicit native "Play next" path; otherwise delete the duplicate local state. + - Acceptance: Now Playing matches what the native player is doing. +4. **Playlist owner names.** Resolve through the authenticated user profile and cache, not on + every collection open. +5. **Test coverage.** + - `NativeSpotifyCatalog` JSON parsing and error mapping (auth, network, malformed response). + - `NativeSpotifyCatalog`/`SpotifyViewModel` event state machine, including `stopped`. + - Device smoke checklist for sign-in, library filters, search, playback, queue, lock screen, + and token expiry. + - Acceptance: JVM tests run in CI; device checklist stays manual. + +### P2 — polish + +- Clear artwork and credential caches on sign-out; expose cache size in Settings. +- Accessibility/focus review for long playlist/show collections and the Library Options filter. +- APK-size review once the vendored librespot patch can be dropped. + +--- + +## 6. Native build, provenance, and licensing + +**Native bridge.** `native/sidekit-spotify` links librespot 0.8.0 and exposes the JNI surface +declared in `apps/spotify/bridge/NativeBridge.kt`. + +- Toolchain: Rust stable, `cargo-ndk` 4.1.2, NDK 27.1.12297006, targets + `aarch64-linux-android` and `x86_64-linux-android`. +- Gradle builds it with `:app:buildSpotifyNative` as part of `assembleDebug`/`assembleRelease`. + If Rust or the NDK is missing, the task is skipped, the APK has no library, and + `NativeLibraries.load("sidekit_spotify")` returns false. +- `Cargo.lock` pins the full graph. `vergen` is held at 9.0.6; do not update it without rebuilding + the Android targets. +- Playback temp files use Android `cacheDir` (`NativeBridge.setTmpDir`). + +**librespot patch.** One minimal in-tree patch is applied via +`native/vendor/librespot-core` and `[patch.crates-io]`: + +- `src/config.rs`: `OS = "linux"` instead of `std::env::consts::OS`. +- Reason: the stored OAuth credential comes from Spotify's desktop/keymaster client id. Login5 + and client-token requests bind to the platform-implied client id; on Android librespot + otherwise reports `INVALID_CREDENTIALS`/`BAD_REQUEST`. +- Scope: one constant. No protocol, playback, audio, or auth logic changes. +- Removal: delete the patch once librespot supports overriding platform/client identity, or once + Sidetracks performs its own Android-client OAuth. +- Verify with `diff -rq ~/.cargo/registry/src/index.crates.io-*/librespot-core-0.8.0 + native/vendor/librespot-core`; only `src/config.rs` (plus registry bookkeeping) should differ. + +**Licensing.** Sidekit and librespot are MIT; librespot's notice and `Cargo.lock` are retained. +Sidespot is GPLv3 and remains reference-only at the untracked `.sidespot/` path: never committed, +linked, vendored, or used as a build dependency. No Sidespot source, identifiers, comments, +assets, or documentation enter Sidekit. + +**Clean-room boundary.** The implementation was written from Sidekit's design system, public +Spotify/librespot documentation, and observable behaviour. Before merge, run the similarity +review: no `sidespot` identifiers in committed code, no copied strings/assets/symbols, and no +structure that only matches the reference implementation. Record the reviewer and date here. diff --git a/native/sidekit-spotify/README.md b/native/sidekit-spotify/README.md index bbe396b0..6a7c6eef 100644 --- a/native/sidekit-spotify/README.md +++ b/native/sidekit-spotify/README.md @@ -35,4 +35,6 @@ the task and the APK has no library; `NativeLibraries.load("sidekit_spotify")` t `vergen-gitcl` 1.0.8 depends on `vergen-lib` 0.1.6 while `vergen` 9.1.0 pulled a second incompatible `vergen-lib`. Do not update that crate without rebuilding Android targets. -No librespot patches are applied. Upstream MIT licence: `LICENSE-librespot`. +One minimal librespot-core patch is applied through `native/vendor/librespot-core` (platform +constant set to `"linux"` so Login5/client-token requests match the desktop client id). It is +documented in `docs/spotify/SIDETRACKS.md` §6. Upstream MIT licence: `LICENSE-librespot`. From ca07dff38d6336e8b1947a082edce70b4f0eb35a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 12:28:05 +0100 Subject: [PATCH 24/46] fix(sidetracks): keep notification transport working without UI --- .../apps/spotify/data/SpotifySession.kt | 46 ++++++++++++++++++- .../apps/spotify/playback/SpotifyPlayback.kt | 12 +++-- .../apps/spotify/SpotifyPlaybackTest.kt | 31 +++++++++++++ docs/spotify/SIDETRACKS.md | 21 +++------ 4 files changed, 91 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index f801b398..2c609e17 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -2,6 +2,8 @@ package hrln.interactive.sidekit.apps.spotify.data import android.content.Context import hrln.interactive.sidekit.apps.spotify.AudioQuality +import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack import hrln.interactive.sidekit.apps.spotify.auth.EncryptedSpotifyCredentialStore import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge @@ -57,7 +59,12 @@ class SpotifySession( scope = playbackScope, audio = SpotifyAudioSink(NativeBridge, playbackScope), context = context?.applicationContext - ) + ).also { player -> + // Adopt transport actions when no ViewModel is attached. The ViewModel installs its + // own callbacks while it exists and clears them in onCleared(). + player.fallbackNext = { skipNext() } + player.fallbackPrevious = { skipPrevious() } + } } init { @@ -126,6 +133,43 @@ class SpotifySession( playbackScope.launch { runCatching { playback.stop() } } } + /** + * Advances the queue when the notification/lock screen has no ViewModel to call back into. + * The ViewModel follows the same queue semantics, so the UI adopts this state on return. + */ + fun skipNext() { + val before = queue.snapshot.current ?: return + val snapshot = queue.next(auto = false) + if (snapshot.current !== before) snapshot.current?.let(::startFromTransport) + } + + /** Restarts the current item past the 3 s threshold, otherwise returns to queue history. */ + fun skipPrevious() { + val current = queue.snapshot.current ?: return + if (playback.position > 3_000L) { + playbackScope.launch { runCatching { seek(0L) } } + return + } + val snapshot = queue.previous(playback.position) + if (snapshot.current !== current) { + snapshot.current?.let(::startFromTransport) + } else { + playbackScope.launch { runCatching { seek(0L) } } + } + } + + private fun startFromTransport(item: SpotifyMediaItem) { + if (item is SpotifyTrack) history.record(item, System.currentTimeMillis()) + playback.setNowPlaying(item, 0L) + val uri = item.uri ?: return + playbackScope.launch { + runCatching { play(uri, 0L) }.onFailure { + playback.pause() + playback.updateState(false, 0L) + } + } + } + /** One librespot player event, or null when nothing has happened since the last poll. */ fun pollPlaybackEvent(): String? = if (nativeAvailable) NativeBridge.pollEvent() else null diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index 845ef378..bab98ac9 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -58,10 +58,16 @@ class SpotifyPlayback( override val isActive: Boolean get() = playing - /** Queue ops still live in the ViewModel; the notification and lock screen call back into it. */ + /** + * Queue ops normally live in the ViewModel so its screen state stays in sync. When the + * ViewModel is gone, [fallbackNext]/[fallbackPrevious] keep the notification and lock-screen + * transport working by advancing the process-wide queue instead. + */ var onNext: (() -> Unit)? = null var onPrevious: (() -> Unit)? = null var onSeek: ((Long) -> Unit)? = null + var fallbackNext: (() -> Unit)? = null + var fallbackPrevious: (() -> Unit)? = null private var mediaSession: MediaSession? = null private var artJob: Job? = null @@ -180,9 +186,9 @@ class SpotifyPlayback( if (callback != null) callback(positionMs) else scope.launch { runCatching { seek(positionMs) } } } - fun requestNext() = onNext?.invoke() + fun requestNext() = (onNext ?: fallbackNext)?.invoke() - fun requestPrevious() = onPrevious?.invoke() + fun requestPrevious() = (onPrevious ?: fallbackPrevious)?.invoke() suspend fun stop() { playing = false diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt index c5fbe165..ca91410c 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt @@ -74,6 +74,37 @@ class SpotifyPlaybackTest { assertTrue(gateway.stopped) } + @Test fun `requestNext prefers the attached UI owner`() { + val playback = SpotifyPlayback( + FakeGateway(), + CoroutineScope(StandardTestDispatcher()), + MediaSessionRegistry() + ) + var uiCalls = 0 + var fallbackCalls = 0 + playback.onNext = { uiCalls++ } + playback.fallbackNext = { fallbackCalls++ } + + playback.requestNext() + + assertEquals(1, uiCalls) + assertEquals(0, fallbackCalls) + } + + @Test fun `requestNext falls back when the UI owner is gone`() { + val playback = SpotifyPlayback( + FakeGateway(), + CoroutineScope(StandardTestDispatcher()), + MediaSessionRegistry() + ) + var fallbackCalls = 0 + playback.fallbackNext = { fallbackCalls++ } + + playback.requestNext() + + assertEquals(1, fallbackCalls) + } + private class FakeGateway(private val failLoad: Boolean = false) : SpotifyGateway { var loadedUri: String? = null var autoplay = false diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index 68c8c7a6..d5d1c602 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -68,7 +68,8 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac ViewModel adopts the live item, position, and playing state. Starting a new item releases the previous `AudioTrack`. - Foreground `mediaPlayback` service and platform `MediaSession` keep librespot alive with the - screen off. Notification shows artwork, title, artist, and previous/play-pause/next. + screen off. Notification shows artwork, title, artist, and previous/play-pause/next. When the + ViewModel is gone, next/previous fall back to advancing the process-wide queue directly. - Sidetracks and local Music pause each other through `MediaSessions`; Sidetracks takes and abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. - OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, @@ -100,8 +101,6 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac search 20. These truncate silently today. 5. **Native `stopped` event is unhandled**, so stopping from the notification can leave a stale track on screen. -6. **Doc drift.** `native/sidekit-spotify/README.md` claims no librespot patch is applied; one - is (see §6). --- @@ -110,23 +109,15 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac Close these before proposing the branch for merge. They are correctness/contract issues, not parity work. -1. **Notification transport without a ViewModel.** Play/pause/stop go through `SpotifySession`, - but next/previous route through `SpotifyViewModel`; if it is cleared those buttons do nothing. - Move queue ownership or a fallback transport into the session/playback layer, or disable the - buttons when no queue owner is attached. -2. **Notification tap target.** The content intent opens `MainActivity` at the launcher. Deep-link +1. **Notification tap target.** The content intent opens `MainActivity` at the launcher. Deep-link it to Sidetracks/Now Playing. -3. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave +2. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave stale UI. -4. **Queue/history persistence.** Persist across process death using the existing atomic-file +3. **Queue/history persistence.** Persist across process death using the existing atomic-file convention, or state the limitation explicitly in the PR. -5. **Align shipped contracts with docs.** Current implementation is authoritative: session id - `"sidetracks"`, channel `"sidetracks"`, notification id `0x6D76`. Update any remaining - `"spotify"`/`0x5354` references. -6. **Service/audio-focus instrumentation.** Cover the foreground service, media session, +4. **Service/audio-focus instrumentation.** Cover the foreground service, media session, audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and screenshots only. -7. **Fix the native README patch claim** and link it to §6 below. --- From 2ec28b25dfb3c39dadefe58dfb3947bfe614f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 12:32:20 +0100 Subject: [PATCH 25/46] fix(sidetracks): open Now Playing from the media notification --- .../hrln/interactive/sidekit/MainActivity.kt | 11 ++++--- .../sidekit/apps/spotify/SpotifyGraph.kt | 29 +++++++++++++++++++ .../playback/SpotifyPlaybackService.kt | 5 +++- .../sidekit/navigation/SidekitNavHost.kt | 3 ++ .../apps/spotify/SidetracksLinksTest.kt | 20 +++++++++++++ docs/spotify/SIDETRACKS.md | 9 +++--- 6 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/MainActivity.kt b/app/src/main/java/hrln/interactive/sidekit/MainActivity.kt index b4bda5c8..11da29f5 100644 --- a/app/src/main/java/hrln/interactive/sidekit/MainActivity.kt +++ b/app/src/main/java/hrln/interactive/sidekit/MainActivity.kt @@ -18,6 +18,7 @@ import hrln.interactive.sidekit.apps.settings.rememberCaseColour import hrln.interactive.sidekit.apps.settings.rememberCompactLayout import hrln.interactive.sidekit.apps.sidemessage.IncomingLinks import hrln.interactive.sidekit.apps.sideport.SideportLinks +import hrln.interactive.sidekit.apps.spotify.SidetracksLinks import hrln.interactive.sidekit.design.action.ChromeState import hrln.interactive.sidekit.design.canvas.SidekitCanvas import hrln.interactive.sidekit.design.input.Keypad @@ -40,8 +41,10 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // Opened from an sms: link or a message notification: go to that conversation. - if (savedInstanceState == null && !SideportLinks.handle(intent)) IncomingLinks.handle(intent) + // Opened from an sms: link, a message notification, or the playback notification. + if (savedInstanceState == null && !SideportLinks.handle(intent) && !IncomingLinks.handle(intent)) { + SidetracksLinks.handle(intent) + } enableEdgeToEdge() // SideKit uses the whole display; the launcher header carries the clock. WindowCompat.getInsetsController(window, window.decorView).apply { @@ -110,8 +113,8 @@ class MainActivity : ComponentActivity() { // singleTask: pressing Home while an app is open delivers the HOME intent here. if (intent.hasCategory(Intent.CATEGORY_HOME)) { homeRequests.intValue++ - } else if (!SideportLinks.handle(intent)) { - IncomingLinks.handle(intent) + } else if (!SideportLinks.handle(intent) && !IncomingLinks.handle(intent)) { + SidetracksLinks.handle(intent) } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 38150a86..34ca21c1 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -5,6 +5,7 @@ import android.net.Uri import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext @@ -25,6 +26,7 @@ import hrln.interactive.sidekit.design.components.SidekitScaffold import hrln.interactive.sidekit.navigation.LAUNCHER_ROUTE import hrln.interactive.sidekit.navigation.SubApp import hrln.interactive.sidekit.ui.UndoWindowLifecycle +import kotlinx.coroutines.flow.MutableStateFlow private val ROOT = SubApp.Sidetracks.route private val HOME = "$ROOT/home" @@ -40,6 +42,33 @@ private val SIGN_IN = "$ROOT/sign-in" private fun collectionRoute(type: SpotifyCollectionType, id: String) = "$ROOT/collection/${type.route}/$id" private fun artistRoute(id: String) = "$ROOT/artist/$id" +/** + * A tap on the playback notification opens Sidetracks at Now Playing instead of the launcher. + * The activity records the request here; the nav host consumes it once it is composed. + */ +object SidetracksLinks { + const val ACTION_OPEN_NOW_PLAYING = "hrln.interactive.sidekit.action.SIDETRACKS_NOW_PLAYING" + + private val pending = MutableStateFlow(false) + + fun handle(intent: Intent?): Boolean { + if (intent?.action != ACTION_OPEN_NOW_PLAYING) return false + pending.value = true + return true + } + + @Composable + fun Follow(nav: NavHostController) { + val open by pending.collectAsState() + LaunchedEffect(open) { + if (!open) return@LaunchedEffect + pending.value = false + nav.navigate(ROOT) { popUpTo(LAUNCHER_ROUTE) } + nav.navigate(NOW_PLAYING) { launchSingleTop = true } + } + } +} + private val collectionArgs = listOf( navArgument("type") { type = NavType.StringType }, navArgument("id") { type = NavType.StringType } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt index c11c4176..de9f7175 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt @@ -11,6 +11,7 @@ import android.content.pm.ServiceInfo import android.graphics.drawable.Icon import android.os.IBinder import hrln.interactive.sidekit.MainActivity +import hrln.interactive.sidekit.apps.spotify.SidetracksLinks import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions import hrln.interactive.sidekit.design.icons.SidekitIcon @@ -86,7 +87,9 @@ class SpotifyPlaybackService : Service() { val open = PendingIntent.getActivity( this, 0, - Intent(this, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), + Intent(this, MainActivity::class.java) + .setAction(SidetracksLinks.ACTION_OPEN_NOW_PLAYING) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_IMMUTABLE ) return Notification.Builder(this, playback.notificationChannelId) diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt index 3fb75cf9..cab07108 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt @@ -32,6 +32,7 @@ import hrln.interactive.sidekit.apps.sideport.openPorted import hrln.interactive.sidekit.apps.sideport.sideportGraph import hrln.interactive.sidekit.apps.sideticks.sideticksGraph import hrln.interactive.sidekit.apps.sideweather.sideweatherGraph +import hrln.interactive.sidekit.apps.spotify.SidetracksLinks import hrln.interactive.sidekit.apps.spotify.sidetracksGraph import hrln.interactive.sidekit.apps.translate.translateGraph import hrln.interactive.sidekit.apps.tripplanning.tripPlanningGraph @@ -56,6 +57,8 @@ fun SidekitNavHost(navController: NavHostController = rememberNavController(), h IncomingLinks.Follow(navController) // A projected app's "Messages and media" opens its Sideport hub. SideportLinks.Follow(navController) + // Tapping the Sidetracks playback notification opens Now Playing. + SidetracksLinks.Follow(navController) // No screen transition. Every screen is the same frame (title, body, action bar) and the // title and bar are shared chrome that morph on their own, so navigating swaps only the body, // which plays its own entrance (rows rising in). Nothing moves as a whole screen, and only one diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt new file mode 100644 index 00000000..e2744e64 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt @@ -0,0 +1,20 @@ +package hrln.interactive.sidekit.apps.spotify + +import android.content.Intent +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SidetracksLinksTest { + @Test fun `the playback notification action asks for now playing`() { + assertTrue(SidetracksLinks.handle(Intent(SidetracksLinks.ACTION_OPEN_NOW_PLAYING))) + } + + @Test fun `unrelated intents are ignored`() { + assertFalse(SidetracksLinks.handle(Intent(Intent.ACTION_VIEW))) + assertFalse(SidetracksLinks.handle(null)) + } +} diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index d5d1c602..a2503b5d 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -70,6 +70,7 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac - Foreground `mediaPlayback` service and platform `MediaSession` keep librespot alive with the screen off. Notification shows artwork, title, artist, and previous/play-pause/next. When the ViewModel is gone, next/previous fall back to advancing the process-wide queue directly. + Tapping the notification opens Sidetracks directly at Now Playing rather than the launcher. - Sidetracks and local Music pause each other through `MediaSessions`; Sidetracks takes and abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. - OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, @@ -109,13 +110,11 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac Close these before proposing the branch for merge. They are correctness/contract issues, not parity work. -1. **Notification tap target.** The content intent opens `MainActivity` at the launcher. Deep-link - it to Sidetracks/Now Playing. -2. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave +1. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave stale UI. -3. **Queue/history persistence.** Persist across process death using the existing atomic-file +2. **Queue/history persistence.** Persist across process death using the existing atomic-file convention, or state the limitation explicitly in the PR. -4. **Service/audio-focus instrumentation.** Cover the foreground service, media session, +3. **Service/audio-focus instrumentation.** Cover the foreground service, media session, audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and screenshots only. From 7fbe9d6786ad122e24d6bf3853c71f24b11725dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 12:35:30 +0100 Subject: [PATCH 26/46] fix(sidetracks): clear playback state on native stop --- .../sidekit/apps/spotify/SpotifyViewModel.kt | 7 +++++++ .../sidekit/apps/spotify/data/SpotifySession.kt | 2 ++ .../sidekit/apps/spotify/playback/SpotifyPlayback.kt | 4 ++++ .../sidekit/apps/spotify/SpotifyPlaybackTest.kt | 3 +++ docs/spotify/SIDETRACKS.md | 10 ++++------ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index d1e52b5f..510c2a3b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -775,6 +775,13 @@ class SpotifyViewModel( "ended" -> if (matchesCurrent(event)) onPlaybackEnded() + "stopped" -> if (matchesCurrent(event)) { + // Stop from the notification or lock screen: clear both the UI and the queue so + // reopening Sidetracks cannot restore the track that was stopped. + session?.playback?.updateState(false, 0L) + applyQueue(queue.stop(), positionMs = 0L, playing = false) + } + "unavailable" -> if (matchesCurrent(event)) { _state.update { it.copy(isPlaying = false, notice = "This track isn't available") } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 2c609e17..05ea727f 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -64,6 +64,7 @@ class SpotifySession( // own callbacks while it exists and clears them in onCleared(). player.fallbackNext = { skipNext() } player.fallbackPrevious = { skipPrevious() } + player.onStopped = { queue.stop() } } } @@ -130,6 +131,7 @@ class SpotifySession( } fun stopPlayback() { + queue.stop() playbackScope.launch { runCatching { playback.stop() } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index bab98ac9..f3e10716 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -69,6 +69,9 @@ class SpotifyPlayback( var fallbackNext: (() -> Unit)? = null var fallbackPrevious: (() -> Unit)? = null + /** Lets the process owner clear queue state whenever playback is stopped from any control. */ + var onStopped: (() -> Unit)? = null + private var mediaSession: MediaSession? = null private var artJob: Job? = null private var lastSessionUpdate = 0L @@ -206,6 +209,7 @@ class SpotifyPlayback( current = null notificationArt = null artJob?.cancel() + onStopped?.invoke() publish() } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt index ca91410c..3f8d37d9 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt @@ -65,6 +65,8 @@ class SpotifyPlaybackTest { val gateway = FakeGateway() val registry = MediaSessionRegistry() val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) + var stopped = false + playback.onStopped = { stopped = true } playback.start("spotify:track:a", 0L) playback.stop() @@ -72,6 +74,7 @@ class SpotifyPlaybackTest { assertFalse(playback.isActive) assertNull(registry.active) assertTrue(gateway.stopped) + assertTrue(stopped) } @Test fun `requestNext prefers the attached UI owner`() { diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index a2503b5d..e55164d7 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -71,6 +71,8 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac screen off. Notification shows artwork, title, artist, and previous/play-pause/next. When the ViewModel is gone, next/previous fall back to advancing the process-wide queue directly. Tapping the notification opens Sidetracks directly at Now Playing rather than the launcher. + A native `stopped` event clears Now Playing and the queue so notification/lock-screen stop + cannot leave stale UI or restore the stopped track. - Sidetracks and local Music pause each other through `MediaSessions`; Sidetracks takes and abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. - OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, @@ -100,8 +102,6 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac 3. **Playlist owner names.** Followed playlists show the owner's Spotify username. 4. **Hard read limits.** Collection 500, playlist tracks 100, show episodes 50, artist albums 50, search 20. These truncate silently today. -5. **Native `stopped` event is unhandled**, so stopping from the notification can leave a stale - track on screen. --- @@ -110,11 +110,9 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac Close these before proposing the branch for merge. They are correctness/contract issues, not parity work. -1. **Handle native `stopped`.** Clear or resync current state so notification stop cannot leave - stale UI. -2. **Queue/history persistence.** Persist across process death using the existing atomic-file +1. **Queue/history persistence.** Persist across process death using the existing atomic-file convention, or state the limitation explicitly in the PR. -3. **Service/audio-focus instrumentation.** Cover the foreground service, media session, +2. **Service/audio-focus instrumentation.** Cover the foreground service, media session, audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and screenshots only. From dd4c119ec3b36c22f97f8d23af35f4cbab76c69f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 12:58:00 +0100 Subject: [PATCH 27/46] feat(sidetracks): persist queue and history and swipe to remove --- .../sidekit/apps/spotify/SpotifyGraph.kt | 1 + .../apps/spotify/SpotifyNowPlayingScreens.kt | 51 +++- .../sidekit/apps/spotify/SpotifyViewModel.kt | 24 +- .../spotify/data/SpotifyHistoryRepository.kt | 5 + .../spotify/data/SpotifyQueueRepository.kt | 176 +++++++------ .../apps/spotify/data/SpotifySession.kt | 18 +- .../apps/spotify/data/SpotifyStateStores.kt | 234 ++++++++++++++++++ .../apps/spotify/SidetracksScreenshotTest.kt | 1 + .../apps/spotify/SpotifyPersistenceTest.kt | 97 ++++++++ .../spotify/SpotifyQueueRepositoryTest.kt | 11 + docs/spotify/SIDETRACKS.md | 14 +- 11 files changed, 520 insertions(+), 112 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 34ca21c1..b8fe5e96 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -234,6 +234,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onJump = vm::jumpTo, onPlayNext = vm::moveToNext, onRemove = vm::removeUpcoming, + onRemoveHistory = vm::removeHistory, onClearUpcoming = vm::clearUpcoming, onRestoreUpcoming = vm::restoreUpcoming, onShuffle = vm::toggleShuffle, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 8092002d..319d82ca 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -34,8 +34,11 @@ import hrln.interactive.sidekit.design.action.undoBar import hrln.interactive.sidekit.design.components.SidekitItemRow import hrln.interactive.sidekit.design.components.SidekitList import hrln.interactive.sidekit.design.components.SidekitScaffold +import hrln.interactive.sidekit.design.components.SidekitSwipeRow import hrln.interactive.sidekit.design.components.SidekitText +import hrln.interactive.sidekit.design.components.SwipeAction import hrln.interactive.sidekit.design.components.listFade +import hrln.interactive.sidekit.design.components.rememberSwipeRevealGroup import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme import kotlinx.coroutines.delay @@ -154,6 +157,16 @@ fun SpotifyNowPlayingScreen( } } +@Composable +private fun removeAction(label: String, onClick: () -> Unit) = SwipeAction( + label = label, + icon = SidekitIcon.DeleteForever, + color = SidekitTheme.colors.dangerA1, + armedColor = SidekitTheme.colors.dangerA2, + destructive = true, + onClick = onClick +) + @Composable fun QueueScreen( state: SpotifyUiState, @@ -162,6 +175,7 @@ fun QueueScreen( onJump: (String) -> Unit, onPlayNext: (SpotifyMediaItem) -> Unit, onRemove: (SpotifyMediaItem) -> Unit, + onRemoveHistory: (SpotifyMediaItem) -> Unit, onClearUpcoming: () -> List, onRestoreUpcoming: (List) -> Unit, onShuffle: () -> Unit, @@ -203,6 +217,7 @@ fun QueueScreen( } SidekitScaffold(title = "Queue", actions = actions, options = options) { padding -> val listState = rememberLazyListState() + val swipes = rememberSwipeRevealGroup() val currentFocus = remember { FocusRequester() } LaunchedEffect(state.current?.id) { if (focusedId == null) runCatching { currentFocus.requestFocus() } } SidekitList(padding, state = listState) { @@ -219,24 +234,38 @@ fun QueueScreen( ) } itemsIndexed(state.upcoming, key = { _, item -> "u${item.id}" }) { index, item -> - SpotifyMediaRow( - item, - Modifier.listFade(listState, index + 1).onFocusChanged { if (it.isFocused) focusedId = item.id }, - secondary = item.creator, - onClick = { onJump(item.id) } - ) - } - if (state.history.isNotEmpty()) { - item(key = "historyLabel") { SectionLabel("History") } - itemsIndexed(state.history.take(20), key = { _, item -> "h${item.id}" }) { index, item -> + SidekitSwipeRow( + key = "u${item.id}", + group = swipes, + modifier = Modifier.listFade(listState, index + 1), + action = removeAction("Remove ${item.title} from queue") { onRemove(item) } + ) { SpotifyMediaRow( item, - Modifier.listFade(listState, index + 2 + state.upcoming.size), + Modifier.onFocusChanged { if (it.isFocused) focusedId = item.id }, secondary = item.creator, onClick = { onJump(item.id) } ) } } + if (state.history.isNotEmpty()) { + item(key = "historyLabel") { SectionLabel("History") } + itemsIndexed(state.history.take(20), key = { _, item -> "h${item.id}" }) { index, item -> + SidekitSwipeRow( + key = "h${item.id}", + group = swipes, + modifier = Modifier.listFade(listState, index + 2 + state.upcoming.size), + action = removeAction("Remove ${item.title} from history") { onRemoveHistory(item) } + ) { + SpotifyMediaRow( + item, + Modifier, + secondary = item.creator, + onClick = { onJump(item.id) } + ) + } + } + } } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 510c2a3b..34e9d20b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -292,8 +292,16 @@ class SpotifyViewModel( if (item.uri == null || session == null) return viewModelScope.launch { try { - if (play) session.resume() else session.pause() - session.playback.updateState(play, _state.value.positionMs) + if (play && session.playback.current == null) { + // A restored queue outlives the native player. Start the current item instead + // of resuming a source that no longer exists. + startRemote(item, 0L) + } else if (play) { + session.resume() + } else { + session.pause() + } + session.playback.updateState(play, if (play) _state.value.positionMs else s.positionMs) } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -549,6 +557,18 @@ class SpotifyViewModel( applyQueue(queue.removeUpcoming(item)) } + fun removeHistory(item: SpotifyMediaItem) { + history.remove(item) + val snapshot = queue.removeHistory(item) + applyQueue(snapshot) + _state.update { + it.copy( + recent = history.items().filterIsInstance(), + notice = "${item.title} removed from history" + ) + } + } + fun addToQueue(item: SpotifyMediaItem) { applyQueue(queue.addToQueue(item)) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt index 4d7602e4..095a3835 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt @@ -39,6 +39,11 @@ class SpotifyHistoryRepository( return snapshot() } + fun remove(item: SpotifyMediaItem): List { + if (entries.removeAll { it.item.id == item.id }) save(entries.toList()) + return snapshot() + } + fun clear(): List { entries.clear() save(emptyList()) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt index 874e6998..99fb07f2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt @@ -12,26 +12,27 @@ import kotlin.random.Random * current item and history never move. The repository is deliberately free of Android and * Compose types; playback, persistence and the UI observe it through [snapshot]. */ -class SpotifyQueueRepository(private val random: Random = Random.Default) { +class SpotifyQueueRepository( + private val random: Random = Random.Default, + private val load: () -> Snapshot = { Snapshot() }, + private val save: (Snapshot) -> Unit = {} +) { data class Snapshot( val current: SpotifyMediaItem? = null, val upcoming: List = emptyList(), val history: List = emptyList(), val shuffle: Boolean = false, val repeat: RepeatMode = RepeatMode.Off, - val context: List = emptyList() + val context: List = emptyList(), + /** Upcoming order before shuffle, kept so shuffle can be turned off after process death. */ + val shuffleBackup: List? = null ) - var snapshot: Snapshot = Snapshot() + var snapshot: Snapshot = runCatching(load).getOrDefault(Snapshot()) private set /** Replaces the queue, used when a new ViewModel adopts the process-wide playback state. */ - fun restore(snapshot: Snapshot): Snapshot { - this.snapshot = snapshot - return snapshot - } - - private var shuffleBackup: List? = null + fun restore(snapshot: Snapshot): Snapshot = commit(snapshot) /** * Starts playback from [index] within [items], replacing the queue. The rows after the @@ -44,54 +45,53 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { val previous = snapshot.current val nextUpcoming = items.drop(index + 1).let { if (snapshot.shuffle) it.shuffled(random) else it } - snapshot = snapshot.copy( - current = item, - upcoming = nextUpcoming, - history = if (previous == null || previous.id == item.id) { - snapshot.history - } else { - (listOf(previous) + snapshot.history).take(HISTORY_LIMIT) - }, - context = items + return commit( + snapshot.copy( + current = item, + upcoming = nextUpcoming, + history = if (previous == null || previous.id == item.id) { + snapshot.history + } else { + (listOf(previous) + snapshot.history).take(HISTORY_LIMIT) + }, + context = items, + shuffleBackup = if (snapshot.shuffle) items.drop(index + 1) else null + ) ) - shuffleBackup = if (snapshot.shuffle) items.drop(index + 1) else null - return snapshot } fun playItem(item: SpotifyMediaItem): Snapshot = play(listOf(item), 0) - /** Stops without clearing the queue; the current item stays so the user can resume. */ - fun stop(): Snapshot { - snapshot = snapshot.copy(current = null, upcoming = emptyList(), history = emptyList(), context = emptyList()) - shuffleBackup = null - return snapshot - } - - fun toggleShuffle(): Snapshot { - if (snapshot.shuffle) { - snapshot = snapshot.copy(shuffle = false, upcoming = shuffleBackup ?: snapshot.upcoming) + /** Stops and clears the queue. */ + fun stop(): Snapshot = commit( + snapshot.copy( + current = null, + upcoming = emptyList(), + history = emptyList(), + context = emptyList(), shuffleBackup = null - } else { - shuffleBackup = snapshot.upcoming - snapshot = snapshot.copy(shuffle = true, upcoming = snapshot.upcoming.shuffled(random)) - } - return snapshot - } + ) + ) - fun clearHistory(): Snapshot { - snapshot = snapshot.copy(history = emptyList()) - return snapshot + fun toggleShuffle(): Snapshot = if (snapshot.shuffle) { + commit( + snapshot.copy( + shuffle = false, + upcoming = snapshot.shuffleBackup ?: snapshot.upcoming, + shuffleBackup = null + ) + ) + } else { + commit(snapshot.copy(shuffle = true, upcoming = snapshot.upcoming.shuffled(random), shuffleBackup = snapshot.upcoming)) } - fun cycleRepeat(): Snapshot { - snapshot = snapshot.copy(repeat = snapshot.repeat.next()) - return snapshot - } + fun clearHistory(): Snapshot = commit(snapshot.copy(history = emptyList())) - fun setRepeat(mode: RepeatMode): Snapshot { - snapshot = snapshot.copy(repeat = mode) - return snapshot - } + fun removeHistory(item: SpotifyMediaItem): Snapshot = commit(snapshot.copy(history = snapshot.history.filterNot { it.id == item.id })) + + fun cycleRepeat(): Snapshot = commit(snapshot.copy(repeat = snapshot.repeat.next())) + + fun setRepeat(mode: RepeatMode): Snapshot = commit(snapshot.copy(repeat = mode)) /** * Moves to the next upcoming item. [auto] distinguishes the end of a track from the user @@ -102,23 +102,19 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { if (auto && snapshot.repeat == RepeatMode.Track) return snapshot val next = snapshot.upcoming.firstOrNull() if (next != null) { - snapshot = snapshot.copy( - current = next, - upcoming = snapshot.upcoming.drop(1), - history = (listOf(current) + snapshot.history).take(HISTORY_LIMIT) + return commit( + snapshot.copy( + current = next, + upcoming = snapshot.upcoming.drop(1), + history = (listOf(current) + snapshot.history).take(HISTORY_LIMIT) + ) ) - return snapshot } if (!auto) return snapshot return when (snapshot.repeat) { RepeatMode.Context -> replayContext() - RepeatMode.Track -> snapshot - - RepeatMode.Off -> { - snapshot = snapshot.copy(current = null, history = snapshot.history) - snapshot - } + RepeatMode.Off -> commit(snapshot.copy(current = null, history = snapshot.history)) } } @@ -127,12 +123,13 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { if (snapshot.current == null) return snapshot if (positionMs > RESTART_THRESHOLD_MS) return snapshot val previous = snapshot.history.firstOrNull() ?: return snapshot - snapshot = snapshot.copy( - current = previous, - upcoming = listOfNotNull(snapshot.current) + snapshot.upcoming, - history = snapshot.history.drop(1) + return commit( + snapshot.copy( + current = previous, + upcoming = listOfNotNull(snapshot.current) + snapshot.upcoming, + history = snapshot.history.drop(1) + ) ) - return snapshot } fun jumpTo(id: String): Snapshot { @@ -142,12 +139,13 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { if (at >= 0) { val skipped = snapshot.upcoming.take(at) val target = snapshot.upcoming[at] - snapshot = snapshot.copy( - current = target, - upcoming = snapshot.upcoming.drop(at + 1), - history = (listOf(current) + skipped + snapshot.history).take(HISTORY_LIMIT) + return commit( + snapshot.copy( + current = target, + upcoming = snapshot.upcoming.drop(at + 1), + history = (listOf(current) + skipped + snapshot.history).take(HISTORY_LIMIT) + ) ) - return snapshot } snapshot.history.firstOrNull { it.id == id }?.let { playItem(it) } return snapshot @@ -157,44 +155,42 @@ class SpotifyQueueRepository(private val random: Random = Random.Default) { fun playNext(item: SpotifyMediaItem): Snapshot { if (item.id == snapshot.current?.id) return snapshot val remaining = snapshot.upcoming.filterNot { it.id == item.id } - snapshot = snapshot.copy(upcoming = listOf(item) + remaining) - return snapshot + return commit(snapshot.copy(upcoming = listOf(item) + remaining)) } - fun addToQueue(item: SpotifyMediaItem): Snapshot { - snapshot = snapshot.copy(upcoming = snapshot.upcoming + item) - return snapshot - } + fun addToQueue(item: SpotifyMediaItem): Snapshot = commit(snapshot.copy(upcoming = snapshot.upcoming + item)) - fun removeUpcoming(item: SpotifyMediaItem): Snapshot { - snapshot = snapshot.copy(upcoming = snapshot.upcoming.filterNot { it.id == item.id }) - return snapshot - } + fun removeUpcoming(item: SpotifyMediaItem): Snapshot = commit(snapshot.copy(upcoming = snapshot.upcoming.filterNot { it.id == item.id })) /** Removes every upcoming item and returns them so the caller can offer Undo. */ fun clearUpcoming(): List { val removed = snapshot.upcoming - snapshot = snapshot.copy(upcoming = emptyList()) - shuffleBackup = null + commit(snapshot.copy(upcoming = emptyList(), shuffleBackup = null)) return removed } /** Restores a cleared tail in front of whatever was appended in the meantime. */ - fun restoreUpcoming(items: List): Snapshot { - snapshot = snapshot.copy(upcoming = items + snapshot.upcoming) - return snapshot - } + fun restoreUpcoming(items: List): Snapshot = commit(snapshot.copy(upcoming = items + snapshot.upcoming)) private fun replayContext(): Snapshot { val items = snapshot.context - if (items.isEmpty()) return snapshot.copy(current = null) + if (items.isEmpty()) return commit(snapshot.copy(current = null)) val first = items.first() - snapshot = snapshot.copy( - current = first, - upcoming = items.drop(1).let { if (snapshot.shuffle) it.shuffled(random) else it }, - history = snapshot.history + return commit( + snapshot.copy( + current = first, + upcoming = items.drop(1).let { if (snapshot.shuffle) it.shuffled(random) else it }, + history = snapshot.history, + shuffleBackup = if (snapshot.shuffle) items.drop(1) else null + ) ) - shuffleBackup = if (snapshot.shuffle) items.drop(1) else null + } + + private fun commit(next: Snapshot): Snapshot { + if (next != snapshot) { + snapshot = next + runCatching { save(next) } + } return snapshot } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 05ea727f..9ac73cf7 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -9,6 +9,7 @@ import hrln.interactive.sidekit.apps.spotify.auth.SpotifyAuth import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge import hrln.interactive.sidekit.apps.spotify.playback.SpotifyAudioSink import hrln.interactive.sidekit.apps.spotify.playback.SpotifyPlayback +import java.io.File import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -31,7 +32,10 @@ class SpotifySession( private val _signedIn = MutableStateFlow(auth.hasCredentials()) val signedIn: StateFlow = _signedIn.asStateFlow() - private val settingsStore = context?.let { SpotifySettingsStore(it.applicationContext) } + private val appContext = context?.applicationContext + private val settingsStore = appContext?.let { SpotifySettingsStore(it) } + private val queueStore = appContext?.let { FileSpotifyQueueStore(File(it.filesDir, "sidetracks-queue.bin")) } + private val historyStore = appContext?.let { FileSpotifyHistoryStore(File(it.filesDir, "sidetracks-history.bin")) } private val _settings = MutableStateFlow(settingsStore?.read() ?: SpotifySettings()) val settings: StateFlow = _settings.asStateFlow() @@ -49,8 +53,14 @@ class SpotifySession( val writer: SpotifyLibraryWriter? get() = catalog as? SpotifyLibraryWriter /** Process-wide queue and history so leaving Sidetracks does not forget what is playing. */ - val queue = SpotifyQueueRepository() - val history = SpotifyHistoryRepository() + val queue = SpotifyQueueRepository( + load = { queueStore?.read() ?: SpotifyQueueRepository.Snapshot() }, + save = { queueStore?.write(it) } + ) + val history = SpotifyHistoryRepository( + load = { historyStore?.read().orEmpty() }, + save = { historyStore?.write(it) } + ) private val playbackScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) val playback: SpotifyPlayback by lazy { @@ -58,7 +68,7 @@ class SpotifySession( gateway = nativeGateway, scope = playbackScope, audio = SpotifyAudioSink(NativeBridge, playbackScope), - context = context?.applicationContext + context = appContext ).also { player -> // Adopt transport actions when no ViewModel is attached. The ViewModel installs its // own callbacks while it exists and clears them in onCleared(). diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt new file mode 100644 index 00000000..fc99722a --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt @@ -0,0 +1,234 @@ +package hrln.interactive.sidekit.apps.spotify.data + +import androidx.core.util.AtomicFile +import hrln.interactive.sidekit.apps.spotify.RepeatMode +import hrln.interactive.sidekit.apps.spotify.SpotifyEpisode +import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem +import hrln.interactive.sidekit.apps.spotify.SpotifyTrack +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.DataInputStream +import java.io.DataOutputStream +import java.io.File +import java.io.IOException + +/** Persists the process-wide queue, including its history and the pre-shuffle order. */ +interface SpotifyQueueStore { + fun read(): SpotifyQueueRepository.Snapshot + fun write(snapshot: SpotifyQueueRepository.Snapshot) +} + +/** Persists the recently played list shown on Home. */ +interface SpotifyHistoryStore { + fun read(): List + fun write(entries: List) +} + +/** + * Small binary files replaced atomically ([AtomicFile]), matching playlists, reminders and + * Sidenotes. A corrupt or missing file falls back to an empty state instead of blocking startup. + */ +class FileSpotifyQueueStore(file: File) : SpotifyQueueStore { + private val file = AtomicFile(file) + + override fun read(): SpotifyQueueRepository.Snapshot { + val stream = try { + file.openRead() + } catch (_: IOException) { + return SpotifyQueueRepository.Snapshot() + } + return try { + DataInputStream(BufferedInputStream(stream)).use { input -> + if (input.readInt() != MAGIC || input.readInt() != VERSION) return SpotifyQueueRepository.Snapshot() + val shuffle = input.readBoolean() + val repeat = RepeatMode.entries.getOrNull(input.readInt()) ?: RepeatMode.Off + SpotifyQueueRepository.Snapshot( + current = input.readItemOrNull(), + upcoming = input.readItems(), + history = input.readItems(), + shuffle = shuffle, + repeat = repeat, + context = input.readItems(), + shuffleBackup = input.readItemsOrNull() + ) + } + } catch (_: Exception) { + SpotifyQueueRepository.Snapshot() + } + } + + override fun write(snapshot: SpotifyQueueRepository.Snapshot) { + val out = file.startWrite() + try { + val data = DataOutputStream(BufferedOutputStream(out)) + data.writeInt(MAGIC) + data.writeInt(VERSION) + data.writeBoolean(snapshot.shuffle) + data.writeInt(snapshot.repeat.ordinal) + data.writeItemOrNull(snapshot.current) + data.writeItems(snapshot.upcoming) + data.writeItems(snapshot.history) + data.writeItems(snapshot.context) + data.writeItemsOrNull(snapshot.shuffleBackup) + data.flush() + file.finishWrite(out) + } catch (e: Exception) { + file.failWrite(out) + throw e + } + } + + private companion object { + const val MAGIC = 0x534B5451 // "SKTQ" + const val VERSION = 1 + } +} + +class FileSpotifyHistoryStore(file: File) : SpotifyHistoryStore { + private val file = AtomicFile(file) + + override fun read(): List { + val stream = try { + file.openRead() + } catch (_: IOException) { + return emptyList() + } + return try { + DataInputStream(BufferedInputStream(stream)).use { input -> + if (input.readInt() != MAGIC || input.readInt() != VERSION) return emptyList() + List(input.readCount(MAX_HISTORY)) { + SpotifyHistoryEntry(input.readItem(), input.readLong()) + } + } + } catch (_: Exception) { + emptyList() + } + } + + override fun write(entries: List) { + val out = file.startWrite() + try { + val data = DataOutputStream(BufferedOutputStream(out)) + data.writeInt(MAGIC) + data.writeInt(VERSION) + data.writeInt(entries.size) + entries.forEach { entry -> + data.writeItem(entry.item) + data.writeLong(entry.playedAtMs) + } + data.flush() + file.finishWrite(out) + } catch (e: Exception) { + file.failWrite(out) + throw e + } + } + + private companion object { + const val MAGIC = 0x534B5448 // "SKTH" + const val VERSION = 1 + const val MAX_HISTORY = 10_000 + } +} + +private const val TYPE_TRACK = 1 +private const val TYPE_EPISODE = 2 +private const val MAX_ITEMS = 100_000 + +private fun DataOutputStream.writeItems(items: List) { + writeInt(items.size) + items.forEach(::writeItem) +} + +private fun DataOutputStream.writeItemsOrNull(items: List?) { + writeBoolean(items != null) + items?.let(::writeItems) +} + +private fun DataOutputStream.writeItemOrNull(item: SpotifyMediaItem?) { + writeBoolean(item != null) + item?.let(::writeItem) +} + +private fun DataOutputStream.writeItem(item: SpotifyMediaItem) { + when (item) { + is SpotifyTrack -> { + writeByte(TYPE_TRACK) + writeUTF(item.id) + writeUTF(item.title) + writeUTF(item.artist) + writeUTF(item.artistId) + writeUTF(item.albumId) + writeInt(item.artRes) + writeLong(item.durationMs) + writeBoolean(item.explicit) + writeBoolean(item.playable) + writeNullableString(item.uri) + writeNullableString(item.artUrl) + } + + is SpotifyEpisode -> { + writeByte(TYPE_EPISODE) + writeUTF(item.id) + writeUTF(item.title) + writeUTF(item.show) + writeUTF(item.showId) + writeUTF(item.published) + writeInt(item.artRes) + writeLong(item.durationMs) + writeBoolean(item.video) + writeNullableString(item.uri) + writeNullableString(item.artUrl) + } + } +} + +private fun DataInputStream.readItems(): List = List(readCount(MAX_ITEMS)) { readItem() } + +private fun DataInputStream.readItemsOrNull(): List? = if (readBoolean()) readItems() else null + +private fun DataInputStream.readItemOrNull(): SpotifyMediaItem? = if (readBoolean()) readItem() else null + +private fun DataInputStream.readItem(): SpotifyMediaItem = when (val type = readUnsignedByte()) { + TYPE_TRACK -> SpotifyTrack( + id = readUTF(), + title = readUTF(), + artist = readUTF(), + artistId = readUTF(), + albumId = readUTF(), + artRes = readInt(), + durationMs = readLong(), + explicit = readBoolean(), + playable = readBoolean(), + uri = readNullableString(), + artUrl = readNullableString() + ) + + TYPE_EPISODE -> SpotifyEpisode( + id = readUTF(), + title = readUTF(), + show = readUTF(), + showId = readUTF(), + published = readUTF(), + artRes = readInt(), + durationMs = readLong(), + video = readBoolean(), + uri = readNullableString(), + artUrl = readNullableString() + ) + + else -> throw IOException("Unknown Sidetracks media item type $type") +} + +private fun DataInputStream.readCount(max: Int): Int { + val count = readInt() + if (count !in 0..max) throw IOException("Invalid Sidetracks item count $count") + return count +} + +private fun DataOutputStream.writeNullableString(value: String?) { + writeBoolean(value != null) + if (value != null) writeUTF(value) +} + +private fun DataInputStream.readNullableString(): String? = if (readBoolean()) readUTF() else null diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt index f9ccd435..de0a9929 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -148,6 +148,7 @@ class SidetracksScreenshotTest { onJump = {}, onPlayNext = {}, onRemove = {}, + onRemoveHistory = {}, onClearUpcoming = { emptyList() }, onRestoreUpcoming = {}, onShuffle = {}, diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt new file mode 100644 index 00000000..d08d68c8 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt @@ -0,0 +1,97 @@ +package hrln.interactive.sidekit.apps.spotify + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import hrln.interactive.sidekit.apps.spotify.data.FileSpotifyHistoryStore +import hrln.interactive.sidekit.apps.spotify.data.FileSpotifyQueueStore +import hrln.interactive.sidekit.apps.spotify.data.SpotifyHistoryRepository +import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository +import java.io.File +import kotlin.random.Random +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class SpotifyPersistenceTest { + private val context: Context = ApplicationProvider.getApplicationContext() + private val tracks = listOf( + track("a"), + track("b"), + track("c"), + track("d") + ) + + @Test fun `queue survives a process restart with shuffle and history`() { + val store = FileSpotifyQueueStore(tempFile("queue")) + val first = SpotifyQueueRepository(Random(1), load = store::read, save = store::write) + first.play(tracks, 0) + first.play(tracks, 1) + first.toggleShuffle() + first.addToQueue(episode("e")) + + val second = SpotifyQueueRepository(Random(2), load = store::read, save = store::write) + + assertEquals(first.snapshot, second.snapshot) + assertEquals("b", second.snapshot.current?.id) + assertEquals(listOf("a"), second.snapshot.history.map { it.id }) + assertTrue(second.snapshot.shuffle) + assertEquals(listOf("c", "d"), second.snapshot.shuffleBackup?.map { it.id }) + assertTrue(second.snapshot.upcoming.last() is SpotifyEpisode) + } + + @Test fun `recently played history survives a process restart`() { + val store = FileSpotifyHistoryStore(tempFile("history")) + val first = SpotifyHistoryRepository(load = store::read, save = store::write) + first.record(track("a"), 10) + first.record(episode("b"), 20) + + val second = SpotifyHistoryRepository(load = store::read, save = store::write) + + assertEquals(listOf("b", "a"), second.items().map { it.id }) + assertEquals(20L, second.snapshot().first().playedAtMs) + } + + @Test fun `removing a history entry persists the shorter list`() { + val store = FileSpotifyHistoryStore(tempFile("history-remove")) + val history = SpotifyHistoryRepository(load = store::read, save = store::write) + history.record(track("a"), 10) + history.record(track("b"), 20) + + history.remove(track("b")) + + assertEquals(listOf("a"), SpotifyHistoryRepository(load = store::read).items().map { it.id }) + } + + @Test fun `a corrupt queue file falls back to an empty queue`() { + val file = tempFile("corrupt").also { it.writeBytes(byteArrayOf(1, 2, 3, 4)) } + + val snapshot = FileSpotifyQueueStore(file).read() + + assertEquals(SpotifyQueueRepository.Snapshot(), snapshot) + } + + private fun tempFile(name: String) = File(context.cacheDir, "sidetracks-$name-${System.nanoTime()}.bin").also { it.delete() } + + private fun track(id: String) = SpotifyTrack( + id = id, + title = "Track $id", + artist = "Artist", + artistId = "artist", + albumId = "album", + artRes = 0, + durationMs = 180_000L + ) + + private fun episode(id: String) = SpotifyEpisode( + id = id, + title = "Episode $id", + show = "Show", + showId = "show", + published = "Today", + artRes = 0, + durationMs = 1_800_000L + ) +} diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt index b0bb42d2..cca58b99 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt @@ -144,6 +144,17 @@ class SpotifyQueueRepositoryTest { assertEquals(listOf("d"), queue.snapshot.upcoming.map { it.id }) } + @Test fun `removing history only drops the chosen item`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.next(auto = false) + queue.next(auto = false) + + queue.removeHistory(track("a")) + + assertEquals(listOf("b"), queue.snapshot.history.map { it.id }) + } + @Test fun `clearing upcoming can be undone`() { val queue = SpotifyQueueRepository(Random(1)) queue.play(tracks, 0) diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index e55164d7..dca21ed8 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -67,6 +67,9 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac - Playback is process-wide. Queue and local history live on `SpotifySession`; a recreated ViewModel adopts the live item, position, and playing state. Starting a new item releases the previous `AudioTrack`. +- Queue, queue history, shuffle order, repeat mode, and recently played persist across process + death in atomic binary files (`sidetracks-queue.bin`, `sidetracks-history.bin`). Playback + itself does not resume on launch. - Foreground `mediaPlayback` service and platform `MediaSession` keep librespot alive with the screen off. Notification shows artwork, title, artist, and previous/play-pause/next. When the ViewModel is gone, next/previous fall back to advancing the process-wide queue directly. @@ -82,7 +85,9 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac - Artwork is memory-safe on device: 300 px URL preference, `RGB_565` + `inSampleSize`, 1024 px cap, `OutOfMemoryError` treated as a cache miss. - Home's action bar opens the queue; track rows across the app expose Add to queue on left swipe. - Library uses the standard single-line header; Settings has one sign-out action in the bar. + Upcoming and History rows in the queue swipe left to remove; removing queue history also drops + the item from recently played. Library uses the standard single-line header; Settings has one + sign-out action in the bar. **Verified** @@ -98,7 +103,8 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac 1. **No recently-played protocol endpoint.** `NativeSpotifyCatalog.recentlyPlayed()` returns empty; Home falls back to local history. 2. **Local queue ownership.** librespot plays one item at a time; Sidekit drives queue, shuffle, - and repeat. Queue and history survive leaving the app but not process death. + and repeat. Queue, queue history, and recently played persist; playback does not resume + automatically after process death. 3. **Playlist owner names.** Followed playlists show the owner's Spotify username. 4. **Hard read limits.** Collection 500, playlist tracks 100, show episodes 50, artist albums 50, search 20. These truncate silently today. @@ -110,9 +116,7 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac Close these before proposing the branch for merge. They are correctness/contract issues, not parity work. -1. **Queue/history persistence.** Persist across process death using the existing atomic-file - convention, or state the limitation explicitly in the PR. -2. **Service/audio-focus instrumentation.** Cover the foreground service, media session, +1. **Service/audio-focus instrumentation.** Cover the foreground service, media session, audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and screenshots only. From 66bde02b78aa3f39d039d80b00dbec57ccb305f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 13:13:46 +0100 Subject: [PATCH 28/46] test(sidetracks): cover foreground service, media session, and unplug --- .../SpotifyPlaybackInstrumentedTest.kt | 152 ++++++++++++++++++ .../SpotifyPlaybackServiceInstrumentedTest.kt | 96 +++++++++++ .../apps/spotify/playback/SpotifyPlayback.kt | 6 +- .../interactive/sidekit/ui/KeypadKeysTest.kt | 26 ++- docs/spotify/SIDETRACKS.md | 15 +- 5 files changed, 273 insertions(+), 22 deletions(-) create mode 100644 app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt create mode 100644 app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt diff --git a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt new file mode 100644 index 00000000..88fdf276 --- /dev/null +++ b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt @@ -0,0 +1,152 @@ +package hrln.interactive.sidekit.apps.spotify + +import android.content.ComponentName +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.PlaybackState +import android.os.SystemClock +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import hrln.interactive.sidekit.apps.media.MediaSessionOwner +import hrln.interactive.sidekit.apps.media.MediaSessionRegistry +import hrln.interactive.sidekit.apps.spotify.data.SpotifyGateway +import hrln.interactive.sidekit.apps.spotify.playback.SpotifyPlayback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SpotifyPlaybackInstrumentedTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val context: Context = ApplicationProvider.getApplicationContext() + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate) + + /** Keeps playback tests from starting the real foreground service. */ + private val silentContext = object : ContextWrapper(context) { + override fun startForegroundService(service: Intent): ComponentName? = null + } + + @Test + fun mediaSessionPublishesMetadataAndPauseState() { + val gateway = FakeGateway() + val playback = SpotifyPlayback(gateway, scope, MediaSessionRegistry(), context = silentContext) + onMain { + playback.setNowPlaying(track("Session track"), 12_000L) + playback.updateState(true, 12_000L) + } + + val controller = MediaController(silentContext, requireNotNull(playback.sessionToken)) + + waitUntil { controller.playbackState?.state == PlaybackState.STATE_PLAYING } + assertEquals("Session track", controller.metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)) + assertTrue((controller.playbackState?.position ?: -1L) >= 12_000L) + + onMain { playback.pause() } + + waitUntil { controller.playbackState?.state == PlaybackState.STATE_PAUSED } + assertFalse(playback.isActive) + assertTrue(gateway.paused) + onMain { playback.setNowPlaying(null) } + } + + @Test + fun activatingAnotherMediaOwnerPausesSidetracks() { + val gateway = FakeGateway() + val registry = MediaSessionRegistry() + val playback = SpotifyPlayback(gateway, scope, registry, context = silentContext) + val music = FakeOwner("music") + registry.register(music) + onMain { + playback.register() + playback.setNowPlaying(track("Handoff"), 0L) + playback.updateState(true, 0L) + } + assertTrue(playback.isActive) + + onMain { + music.isActive = true + registry.activate(music) + } + + waitUntil { !playback.isActive } + assertSame(music, registry.active) + assertTrue(gateway.paused) + onMain { playback.setNowPlaying(null) } + } + + @Test + fun headphoneUnplugPausesPlayback() { + val gateway = FakeGateway() + val playback = SpotifyPlayback(gateway, scope, MediaSessionRegistry(), context = silentContext) + onMain { + playback.setNowPlaying(track("Unplugged"), 0L) + playback.updateState(true, 0L) + } + assertTrue(playback.isActive) + + // The platform protects ACTION_AUDIO_BECOMING_NOISY, so invoke the receiver's callback. + onMain { playback.onAudioBecomingNoisy() } + + waitUntil { !playback.isActive } + assertTrue(gateway.paused) + onMain { playback.setNowPlaying(null) } + } + + private fun onMain(block: () -> Unit) = instrumentation.runOnMainSync(block) + + private fun waitUntil(condition: () -> Boolean) { + val deadline = SystemClock.elapsedRealtime() + 5_000L + while (!condition()) { + if (SystemClock.elapsedRealtime() > deadline) throw AssertionError("Timed out") + Thread.sleep(20L) + } + } + + private fun track(title: String) = SpotifyTrack( + id = "track-${title.hashCode()}", + title = title, + artist = "Artist", + artistId = "artist", + albumId = "album", + artRes = 0, + durationMs = 180_000L, + uri = "spotify:track:test" + ) + + private class FakeGateway : SpotifyGateway { + @Volatile var paused = false + + override suspend fun connect(accessToken: String) = Unit + override suspend fun disconnect() = Unit + override suspend fun load(uri: String, positionMs: Long, autoplay: Boolean) = Unit + override suspend fun play() = Unit + override suspend fun pause() { + paused = true + } + + override suspend fun stop() = Unit + override suspend fun seek(positionMs: Long) = Unit + } + + private class FakeOwner(override val sessionId: String) : MediaSessionOwner { + override val notificationChannelId = "channel-$sessionId" + override val notificationChannelName = "Channel $sessionId" + override val notificationId = sessionId.hashCode() + override var isActive = false + + override fun pause() { + isActive = false + } + } +} diff --git a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt new file mode 100644 index 00000000..cbf29242 --- /dev/null +++ b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt @@ -0,0 +1,96 @@ +package hrln.interactive.sidekit.apps.spotify + +import android.Manifest +import android.app.Notification +import android.app.NotificationManager +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build +import android.os.SystemClock +import androidx.test.core.app.ActivityScenario +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import hrln.interactive.sidekit.MainActivity +import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions +import hrln.interactive.sidekit.apps.spotify.playback.SpotifyPlaybackService +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SpotifyPlaybackServiceInstrumentedTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + private val context: Context = ApplicationProvider.getApplicationContext() + private val session get() = SpotifySessions.get(context) + private val manager get() = context.getSystemService(NotificationManager::class.java) + private val notificationId get() = session.playback.notificationId + + @Before + fun grantNotifications() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && + context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED + ) { + instrumentation.uiAutomation.grantRuntimePermission( + context.packageName, + Manifest.permission.POST_NOTIFICATIONS + ) + } + } + + @After + fun cleanUp() { + onMain { + session.playback.updateState(false, 0L) + session.playback.setNowPlaying(null) + } + context.stopService(Intent(context, SpotifyPlaybackService::class.java)) + manager.cancel(notificationId) + } + + @Test + fun playingTrackStartsTheForegroundServiceAndPublishesTransportActions() { + ActivityScenario.launch(MainActivity::class.java).use { + onMain { + session.playback.setNowPlaying(track) + session.playback.updateState(true, 0L) + } + + val notification = waitForNotification() + assertEquals("sidetracks", notification.channelId) + assertEquals("Service track", notification.extras.getCharSequence(Notification.EXTRA_TITLE)?.toString()) + assertEquals("Artist", notification.extras.getCharSequence(Notification.EXTRA_TEXT)?.toString()) + assertEquals(listOf("Previous", "Pause", "Next"), notification.actions.map { it.title.toString() }) + assertNotNull(notification.contentIntent) + assertTrue(notification.flags and Notification.FLAG_ONGOING_EVENT != 0) + assertTrue(manager.getNotificationChannel("sidetracks") != null) + } + } + + private fun waitForNotification(): Notification { + val deadline = SystemClock.elapsedRealtime() + 5_000L + while (true) { + manager.activeNotifications.firstOrNull { it.id == notificationId }?.let { return it.notification } + if (SystemClock.elapsedRealtime() > deadline) throw AssertionError("Notification was not posted") + Thread.sleep(20L) + } + } + + private fun onMain(block: () -> Unit) = instrumentation.runOnMainSync(block) + + private val track = SpotifyTrack( + id = "service-track", + title = "Service track", + artist = "Artist", + artistId = "artist", + albumId = "album", + artRes = 0, + durationMs = 180_000L, + uri = "spotify:track:service" + ) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index f3e10716..3b48a543 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -78,10 +78,12 @@ class SpotifyPlayback( private var noisyRegistered = false private val noisy = object : BroadcastReceiver() { - // Headphones unplugged: pause rather than play out loud. - override fun onReceive(context: Context, intent: Intent) = pause() + override fun onReceive(context: Context, intent: Intent) = onAudioBecomingNoisy() } + /** Headphones unplugged: pause rather than play out loud. */ + fun onAudioBecomingNoisy() = pause() + private val focus = context?.let { AudioFocus( it, diff --git a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt index 09438b1e..2ebaae3e 100644 --- a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt @@ -3,8 +3,8 @@ package hrln.interactive.sidekit.ui import android.view.KeyEvent import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasContentDescription -import androidx.compose.ui.test.onLast import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule +import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick @@ -18,13 +18,21 @@ import org.junit.runner.RunWith /** The keypad's top keys: top-right (ENDCALL) is Options, top-left (CALL) is Back. */ @RunWith(AndroidJUnit4::class) class KeypadKeysTest { - @get:Rule(order = 0) val completedSetup = object : org.junit.rules.ExternalResource() { + @get:Rule(order = 0) + val completedSetup = object : org.junit.rules.ExternalResource() { override fun before() { val context = androidx.test.core.app.ApplicationProvider.getApplicationContext() - hrln.interactive.sidekit.apps.settings.SidekitPreferences(context).setupComplete = true + hrln.interactive.sidekit.apps.settings.SidekitPreferences(context).apply { + setupComplete = true + // Earlier tests may persist launcher state; these assertions use grid tiles. + listLayout = false + hiddenApps = emptySet() + } } } - @get:Rule(order = 1) val compose = createAndroidComposeRule() + + @get:Rule(order = 1) + val compose = createAndroidComposeRule() private fun press(code: Int): Boolean { var handled = false @@ -75,13 +83,15 @@ class KeypadKeysTest { val keypad = hrln.interactive.sidekit.design.input.Keypad.forTesting( kotlinx.coroutines.CoroutineScope(kotlinx.coroutines.Dispatchers.Unconfined), hrln.interactive.sidekit.design.input.T9Words.Empty, - mapper = hrln.interactive.sidekit.design.input.KeyMapper.create( - compose.activity.getSharedPreferences("sidekit_keypad", android.content.Context.MODE_PRIVATE)), + mapper = hrln.interactive.sidekit.design.input.KeyMapper.create(compose.activity.getSharedPreferences("sidekit_keypad", android.content.Context.MODE_PRIVATE)) ) keypad.useCustomMapping(hrln.interactive.sidekit.design.input.KeypadModule.T9) - keypad.mapper.bind(hrln.interactive.sidekit.design.input.KeypadModule.T9, KeyEvent.KEYCODE_ENDCALL, + keypad.mapper.bind( + hrln.interactive.sidekit.design.input.KeypadModule.T9, + KeyEvent.KEYCODE_ENDCALL, // Test events come from the virtual keyboard device. - hrln.interactive.sidekit.design.input.PhysicalKey("Virtual", KeyEvent.KEYCODE_F1)) + hrln.interactive.sidekit.design.input.PhysicalKey("Virtual", KeyEvent.KEYCODE_F1) + ) } assertTrue(press(KeyEvent.KEYCODE_F1)) compose.onNodeWithContentDescription("Close options").assertIsDisplayed() diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index dca21ed8..f678990b 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -95,6 +95,8 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac - APK contains `arm64-v8a` and `x86_64` `libsidekit_spotify.so`. - On `SP01GE260600886` (480 × 640): libraries load real data, search returns Pathfinder tracks, playback advances/pauses correctly, show episodes format dates, and artwork no longer crashes. +- On `SP01GE260600886`: instrumented tests cover the foreground-service notification and actions, + media-session metadata/state, media-owner hand-off, and the headphone-unplug pause path. --- @@ -111,18 +113,7 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac --- -## 4. Before merge - -Close these before proposing the branch for merge. They are correctness/contract issues, not -parity work. - -1. **Service/audio-focus instrumentation.** Cover the foreground service, media session, - audio-focus hand-off, and headphone unplug on Android. Current tests cover JVM seams and - screenshots only. - ---- - -## 5. What's next +## 4. What's next Ordered by user-visible impact. Each item names its seam so it does not become another cross-cutting refactor. From 171a6b29f02f9744d85259e39a15a92d1469ee10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 13:50:09 +0100 Subject: [PATCH 29/46] feat(sidetracks): page catalog lists in small chunks --- .../sidekit/apps/spotify/SpotifyComponents.kt | 14 + .../sidekit/apps/spotify/SpotifyGraph.kt | 9 + .../apps/spotify/SpotifyLibraryScreens.kt | 28 +- .../sidekit/apps/spotify/SpotifyModels.kt | 22 +- .../apps/spotify/SpotifySearchScreen.kt | 31 ++ .../sidekit/apps/spotify/SpotifyViewModel.kt | 300 +++++++++++++++++- .../apps/spotify/bridge/NativeBridge.kt | 20 +- .../apps/spotify/bridge/SpotifyNative.kt | 10 +- .../apps/spotify/data/NativeSpotifyCatalog.kt | 42 ++- .../apps/spotify/data/SpotifyCatalog.kt | 38 ++- .../apps/spotify/data/SpotifyWebApi.kt | 147 ++++++--- .../apps/spotify/data/SpotifyWebCatalog.kt | 37 ++- .../apps/spotify/MockSpotifyDataSource.kt | 74 +++-- .../apps/spotify/NativeSpotifyCatalogTest.kt | 79 ++++- .../apps/spotify/NativeSpotifyGatewayTest.kt | 10 +- .../apps/spotify/SidetracksScreenshotTest.kt | 15 +- .../apps/spotify/SpotifyPaginationTest.kt | 52 +++ .../interactive/sidekit/ui/KeypadKeysTest.kt | 4 + docs/spotify/SIDETRACKS.md | 19 +- native/sidekit-spotify/src/catalog.rs | 127 +++++--- native/sidekit-spotify/src/lib.rs | 44 ++- 21 files changed, 931 insertions(+), 191 deletions(-) create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPaginationTest.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index a5412701..9c64810c 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -270,6 +271,19 @@ fun TerminalRow(label: String, secondary: String? = null, onClick: (() -> Unit)? ) } +/** + * Loads the next page when the footer enters composition. Lazy lists only compose visible rows + * (plus a small prefetch window), so this keeps both network and rendering work proportional to + * what the user is actually looking at. + */ +@Composable +fun PagingFooter(loading: Boolean, onLoadMore: () -> Unit) { + LaunchedEffect(loading) { + if (!loading) onLoadMore() + } + EmptyMessage(if (loading) "Loading more…" else "More", null) +} + fun withNotice(notice: String?, onDismiss: () -> Unit, actions: List): List = if (notice == null) { actions } else { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index b8fe5e96..44539aad 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -114,6 +114,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onArtist = { nav.navigate(artistRoute(it.id)) }, onShow = { nav.navigate(collectionRoute(SpotifyCollectionType.Show, it.id)) }, + onLoadMore = vm::loadMoreLibrary, onDismissNotice = vm::dismissNotice ) } @@ -142,6 +143,8 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onQueueItem = vm::addToQueue, onNowPlaying = { nav.navigate(NOW_PLAYING) }, onUndo = vm::undo, + onLoadMore = { type?.let { vm.loadMoreCollection(it, id) } }, + loadingMore = "${type?.route}:$id" in state.collectionMorePending, onDismissNotice = vm::dismissNotice ) } @@ -171,6 +174,11 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onFollow = { vm.toggleFollow(artist) }, onNowPlaying = { nav.navigate(NOW_PLAYING) }, onUndo = vm::undo, + onLoadMoreAlbums = { vm.loadMoreArtistAlbums(artist) }, + onLoadMoreTracks = { vm.loadMoreArtistTracks(artist) }, + albumHasMore = state.artistAlbumHasMore[artist.id] == true, + trackHasMore = state.artistTrackHasMore[artist.id] == true, + loadingMore = artist.id in state.artistMorePending, onDismissNotice = vm::dismissNotice ) } @@ -190,6 +198,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { onAlbum = { nav.navigate(collectionRoute(SpotifyCollectionType.Album, it.id)) }, onPlaylist = { nav.navigate(collectionRoute(SpotifyCollectionType.Playlist, it.id)) }, onShow = { nav.navigate(collectionRoute(SpotifyCollectionType.Show, it.id)) }, + onLoadMore = vm::loadMoreSearch, onDismissNotice = vm::dismissNotice ) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 678d4fce..2b8055cc 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -113,6 +113,7 @@ fun LibraryScreen( onAlbum: (SpotifyAlbum) -> Unit, onArtist: (SpotifyArtist) -> Unit, onShow: (SpotifyShow) -> Unit, + onLoadMore: () -> Unit, onDismissNotice: () -> Unit ) { val actions = withNotice( @@ -165,6 +166,11 @@ fun LibraryScreen( else -> Unit } } + if (state.libraryHasMore) { + item(key = "more") { + PagingFooter(state.libraryMorePending, onLoadMore) + } + } } } } @@ -182,6 +188,8 @@ fun CollectionScreen( onQueueItem: (SpotifyMediaItem) -> Unit, onNowPlaying: () -> Unit, onUndo: () -> Unit, + onLoadMore: () -> Unit, + loadingMore: Boolean, onDismissNotice: () -> Unit ) { val colors = SidekitTheme.colors @@ -231,7 +239,7 @@ fun CollectionScreen( maxLines = 1 ) } - itemsIndexed(collection.items, key = { _, item -> item.id }) { index, item -> + itemsIndexed(collection.items, key = { index, item -> "$index:${item.id}" }) { index, item -> SidekitSwipeRow( key = item.id, group = swipes, @@ -248,6 +256,11 @@ fun CollectionScreen( ) } } + if (collection.hasMore) { + item(key = "more") { + PagingFooter(loadingMore, onLoadMore) + } + } } } } @@ -266,6 +279,11 @@ fun ArtistScreen( onFollow: () -> Unit, onNowPlaying: () -> Unit, onUndo: () -> Unit, + onLoadMoreAlbums: () -> Unit, + onLoadMoreTracks: () -> Unit, + albumHasMore: Boolean, + trackHasMore: Boolean, + loadingMore: Boolean, onDismissNotice: () -> Unit ) { val followed = artist.id in state.followedArtistIds @@ -288,7 +306,7 @@ fun ArtistScreen( } } item(key = "topLabel") { SectionLabel("Top tracks") } - itemsIndexed(tracks.take(10), key = { _, track -> track.id }) { index, track -> + itemsIndexed(tracks, key = { _, track -> track.id }) { index, track -> SidekitSwipeRow( key = track.id, group = swipes, @@ -298,6 +316,9 @@ fun ArtistScreen( SpotifyMediaRow(track, Modifier, secondary = track.artist, onClick = { onTrack(track) }) } } + if (trackHasMore) { + item(key = "tracksMore") { PagingFooter(loadingMore, onLoadMoreTracks) } + } item(key = "albumsLabel") { SectionLabel("Albums") } itemsIndexed(albums, key = { _, album -> album.id }) { index, album -> SpotifyLibraryRow( @@ -308,6 +329,9 @@ fun ArtistScreen( artUrl = album.artUrl ) { onAlbum(album) } } + if (albumHasMore) { + item(key = "albumsMore") { PagingFooter(loadingMore, onLoadMoreAlbums) } + } } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt index c47b1379..ee3f5b63 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyModels.kt @@ -115,9 +115,29 @@ data class SpotifyCollection( val artRes: Int, val items: List, val artistId: String? = null, - val artUrl: String? = null + val artUrl: String? = null, + val totalItems: Int = items.size, + val hasMore: Boolean = false ) +/** How many rows a catalog request asks for. Kept small for the Sidephone's weak CPU. */ +const val SPOTIFY_PAGE_SIZE = 50 + +/** One page of a catalog list. [total] is null when the endpoint doesn't report it. */ +data class SpotifyPage( + val items: List = emptyList(), + val total: Int? = null, + val hasMore: Boolean = false +) { + companion object { + fun of(items: List, offset: Int, limit: Int, total: Int? = null): SpotifyPage = SpotifyPage( + items = items, + total = total, + hasMore = if (total != null) offset + items.size < total else items.size >= limit + ) + } +} + data class SpotifySearchResults( val tracks: List = emptyList(), val artists: List = emptyList(), diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 0b194d98..1b6c19ed 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -42,6 +42,7 @@ fun SpotifySearchScreen( onAlbum: (SpotifyAlbum) -> Unit, onPlaylist: (SpotifyPlaylist) -> Unit, onShow: (SpotifyShow) -> Unit, + onLoadMore: (SearchScope) -> Unit, onDismissNotice: () -> Unit ) { var expandedTracks by remember(state.query) { mutableStateOf(false) } @@ -131,6 +132,9 @@ fun SpotifySearchScreen( if (!expandedTracks && results.tracks.size > 5) { item(key = "moreTracks") { TerminalRow("More tracks", secondary = "${results.tracks.size - 5} more", onClick = { expandedTracks = true }) } } + if (expandedTracks && SearchScope.Tracks in state.searchHasMore) { + item(key = "moreTrackPages") { SearchMoreRow(SearchScope.Tracks, "More tracks", state, onLoadMore) } + } } if (results.artists.isNotEmpty()) { item(key = "artistsLabel") { SectionLabel("Artists") } @@ -138,6 +142,9 @@ fun SpotifySearchScreen( SpotifyLibraryRow(artist.artRes, artist.name, "Artist", Modifier.listFade(listState, slot + index), artUrl = artist.artUrl) { onArtist(artist) } } slot += results.artists.size + if (SearchScope.Artists in state.searchHasMore) { + item(key = "moreArtists") { SearchMoreRow(SearchScope.Artists, "More artists", state, onLoadMore) } + } } if (results.albums.isNotEmpty()) { item(key = "albumsLabel") { SectionLabel("Albums") } @@ -145,6 +152,9 @@ fun SpotifySearchScreen( SpotifyLibraryRow(album.artRes, album.title, "${album.artist} · ${album.year}", Modifier.listFade(listState, slot + index), artUrl = album.artUrl) { onAlbum(album) } } slot += results.albums.size + if (SearchScope.Albums in state.searchHasMore) { + item(key = "moreAlbums") { SearchMoreRow(SearchScope.Albums, "More albums", state, onLoadMore) } + } } if (results.playlists.isNotEmpty()) { item(key = "playlistsLabel") { SectionLabel("Playlists") } @@ -152,15 +162,36 @@ fun SpotifySearchScreen( SpotifyLibraryRow(playlist.artRes, playlist.name, "${playlist.owner} · ${playlist.trackCount} tracks", Modifier.listFade(listState, slot + index), artUrl = playlist.artUrl) { onPlaylist(playlist) } } slot += results.playlists.size + if (SearchScope.Playlists in state.searchHasMore) { + item(key = "morePlaylists") { SearchMoreRow(SearchScope.Playlists, "More playlists", state, onLoadMore) } + } } if (results.shows.isNotEmpty()) { item(key = "showsLabel") { SectionLabel("Shows") } itemsIndexed(results.shows, key = { _, show -> "s${show.id}" }) { index, show -> SpotifyLibraryRow(show.artRes, show.name, show.publisher, Modifier.listFade(listState, slot + index), artUrl = show.artUrl) { onShow(show) } } + if (SearchScope.Shows in state.searchHasMore) { + item(key = "moreShows") { SearchMoreRow(SearchScope.Shows, "More shows", state, onLoadMore) } + } } } } } } } + +@Composable +private fun SearchMoreRow( + scope: SearchScope, + label: String, + state: SpotifyUiState, + onLoadMore: (SearchScope) -> Unit +) { + val loading = scope in state.searchMorePending + TerminalRow( + label, + secondary = if (loading) "Loading…" else null, + onClick = if (loading) null else ({ onLoadMore(scope) }) + ) +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 34e9d20b..7c1ae7a9 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -50,6 +50,14 @@ data class SpotifyUiState( val searchPending: Boolean = false, val authPending: Boolean = false, val libraryLoading: Boolean = false, + val libraryHasMore: Boolean = false, + val libraryMorePending: Boolean = false, + val searchHasMore: Set = emptySet(), + val searchMorePending: Set = emptySet(), + val collectionMorePending: Set = emptySet(), + val artistAlbumHasMore: Map = emptyMap(), + val artistTrackHasMore: Map = emptyMap(), + val artistMorePending: Set = emptySet(), val isPlaying: Boolean = false, val positionMs: Long = 0L, val shuffle: Boolean = false, @@ -192,7 +200,7 @@ class SpotifyViewModel( _state.update { it.copy(pendingCollections = it.pendingCollections + key) } viewModelScope.launch { try { - val collection = catalog.collection(type, id) + val collection = catalog.collection(type, id, 0, SPOTIFY_PAGE_SIZE) val saved = if (collection == null) null else writer?.isSaved(type, id) _state.update { val keys = it.savedCollectionKeys.toMutableSet() @@ -215,6 +223,120 @@ class SpotifyViewModel( } } + fun loadMoreCollection(type: SpotifyCollectionType, id: String) { + val key = collectionKey(type, id) + val snapshot = _state.value + val collection = snapshot.collections[key] ?: return + if (!collection.hasMore || key in snapshot.collectionMorePending) return + _state.update { it.copy(collectionMorePending = it.collectionMorePending + key) } + viewModelScope.launch { + try { + val next = catalog.collection(type, id, collection.items.size, SPOTIFY_PAGE_SIZE) + _state.update { state -> + val current = state.collections[key] ?: return@update state + if (next == null) { + state.copy( + collectionMorePending = state.collectionMorePending - key, + collections = state.collections + (key to current.copy(hasMore = false)) + ) + } else { + state.copy( + collectionMorePending = state.collectionMorePending - key, + collections = state.collections + (key to current.append(next)) + ) + } + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + val current = it.collections[key] + it.copy( + collectionMorePending = it.collectionMorePending - key, + collections = if (current == null) it.collections else it.collections + (key to current.copy(hasMore = false)), + notice = playbackNotice(e) + ) + } + } + } + } + + fun loadMoreArtistAlbums(artist: SpotifyArtist) { + val id = artist.id + val snapshot = _state.value + if (snapshot.artistAlbumHasMore[id] != true || id in snapshot.artistMorePending) return + _state.update { it.copy(artistMorePending = it.artistMorePending + id) } + viewModelScope.launch { + try { + val page = catalog.albumsBy(artist, snapshot.artistAlbums[id].orEmpty().size, SPOTIFY_PAGE_SIZE) + _state.update { + it.copy( + artistAlbums = it.artistAlbums + (id to it.artistAlbums[id].orEmpty() + page.items), + artistAlbumHasMore = it.artistAlbumHasMore + (id to page.hasMore), + artistMorePending = it.artistMorePending - id + ) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy( + artistAlbumHasMore = it.artistAlbumHasMore + (id to false), + artistMorePending = it.artistMorePending - id, + notice = playbackNotice(e) + ) + } + } + } + } + + fun loadMoreArtistTracks(artist: SpotifyArtist) { + val id = artist.id + val snapshot = _state.value + if (snapshot.artistTrackHasMore[id] != true || id in snapshot.artistMorePending) return + _state.update { it.copy(artistMorePending = it.artistMorePending + id) } + viewModelScope.launch { + try { + val page = catalog.topTracks(artist, snapshot.artistTracks[id].orEmpty().size, SPOTIFY_PAGE_SIZE) + _state.update { + it.copy( + artistTracks = it.artistTracks + (id to it.artistTracks[id].orEmpty() + page.items), + artistTrackHasMore = it.artistTrackHasMore + (id to page.hasMore), + artistMorePending = it.artistMorePending - id + ) + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy( + artistTrackHasMore = it.artistTrackHasMore + (id to false), + artistMorePending = it.artistMorePending - id, + notice = playbackNotice(e) + ) + } + } + } + } + + private suspend fun loadAllCollection(collection: SpotifyCollection): SpotifyCollection { + var current = collection + while (current.hasMore) { + val next = catalog.collection(current.type, current.id, current.items.size, SPOTIFY_PAGE_SIZE) ?: break + current = current.append(next) + _state.update { + it.copy(collections = it.collections + (collectionKey(current.type, current.id) to current)) + } + } + return current + } + + private fun SpotifyCollection.append(next: SpotifyCollection): SpotifyCollection = copy( + items = items + next.items, + totalItems = next.totalItems, + hasMore = next.hasMore + ) + fun ensureArtist(id: String) { val snapshot = _state.value if (id in snapshot.artists || id in snapshot.pendingArtists) return @@ -226,8 +348,8 @@ class SpotifyViewModel( _state.update { it.copy(pendingArtists = it.pendingArtists - id) } return@launch } - val albums = catalog.albumsBy(artist) - val tracks = catalog.topTracks(artist) + val albums = catalog.albumsBy(artist, 0, SPOTIFY_PAGE_SIZE) + val tracks = catalog.topTracks(artist, 0, SPOTIFY_PAGE_SIZE) val followed = writer?.isArtistFollowed(id) _state.update { val ids = it.followedArtistIds.toMutableSet() @@ -236,8 +358,10 @@ class SpotifyViewModel( } it.copy( artists = it.artists + (id to artist), - artistAlbums = it.artistAlbums + (id to albums), - artistTracks = it.artistTracks + (id to tracks), + artistAlbums = it.artistAlbums + (id to albums.items), + artistTracks = it.artistTracks + (id to tracks.items), + artistAlbumHasMore = it.artistAlbumHasMore + (id to albums.hasMore), + artistTrackHasMore = it.artistTrackHasMore + (id to tracks.hasMore), followedArtistIds = ids, pendingArtists = it.pendingArtists - id ) @@ -274,7 +398,25 @@ class SpotifyViewModel( fun playItem(item: SpotifyMediaItem) = play(listOf(item), 0) - fun playCollection(collection: SpotifyCollection, index: Int) = play(collection.items, index) + fun playCollection(collection: SpotifyCollection, index: Int) { + if (!collection.hasMore) { + play(collection.items, index) + return + } + val key = collectionKey(collection.type, collection.id) + _state.update { it.copy(collectionMorePending = it.collectionMorePending + key) } + viewModelScope.launch { + try { + play(loadAllCollection(collection).items, index) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { it.copy(notice = playbackNotice(e)) } + } finally { + _state.update { it.copy(collectionMorePending = it.collectionMorePending - key) } + } + } + } fun playArtist(artist: SpotifyArtist) = play(topTracks(artist), 0) @@ -520,7 +662,15 @@ class SpotifyViewModel( fun clearQuery() { searchJob?.cancel() - _state.update { it.copy(query = "", searchResults = SpotifySearchResults(), searchPending = false) } + _state.update { + it.copy( + query = "", + searchResults = SpotifySearchResults(), + searchPending = false, + searchHasMore = emptySet(), + searchMorePending = emptySet() + ) + } } fun toggleScope(scope: SearchScope) { @@ -632,11 +782,13 @@ class SpotifyViewModel( try { val filter = _state.value.filter val recent = catalog.recentlyPlayed() - val library = catalog.library(filter) + val library = catalog.library(filter, 0, SPOTIFY_PAGE_SIZE) _state.update { - it.withLibrarySaved(filter, library).copy( + it.withLibrarySaved(filter, library.items).copy( recent = recent.ifEmpty { history.items().filterIsInstance() }, - libraryItems = library, + libraryItems = library.items, + libraryHasMore = library.hasMore, + libraryMorePending = false, libraryLoading = false ) } @@ -650,13 +802,17 @@ class SpotifyViewModel( private fun loadLibrary(filter: LibraryFilter) { if (!_state.value.signedIn) return - _state.update { it.copy(libraryLoading = true) } + _state.update { it.copy(libraryLoading = true, libraryMorePending = false) } viewModelScope.launch { try { - val items = catalog.library(filter) + val page = catalog.library(filter, 0, SPOTIFY_PAGE_SIZE) _state.update { if (it.filter == filter) { - it.withLibrarySaved(filter, items).copy(libraryItems = items, libraryLoading = false) + it.withLibrarySaved(filter, page.items).copy( + libraryItems = page.items, + libraryHasMore = page.hasMore, + libraryLoading = false + ) } else { it } @@ -669,6 +825,37 @@ class SpotifyViewModel( } } + fun loadMoreLibrary() { + val snapshot = _state.value + if (!snapshot.libraryHasMore || snapshot.libraryMorePending || snapshot.libraryLoading) return + val filter = snapshot.filter + val offset = snapshot.libraryItems.size + _state.update { it.copy(libraryMorePending = true) } + viewModelScope.launch { + try { + val page = catalog.library(filter, offset, SPOTIFY_PAGE_SIZE) + _state.update { + if (it.filter != filter) { + it.copy(libraryMorePending = false) + } else { + val merged = (it.libraryItems + page.items).distinctBy(::libraryItemKey) + it.withLibrarySaved(filter, merged).copy( + libraryItems = merged, + libraryHasMore = page.hasMore, + libraryMorePending = false + ) + } + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy(libraryMorePending = false, libraryHasMore = false, notice = playbackNotice(e)) + } + } + } + } + private fun clearAccount() { undoWindow.cancel() queue.stop() @@ -680,8 +867,16 @@ class SpotifyViewModel( it.copy( recent = emptyList(), libraryItems = emptyList(), + libraryHasMore = false, + libraryMorePending = false, searchResults = SpotifySearchResults(), + searchHasMore = emptySet(), + searchMorePending = emptySet(), collections = emptyMap(), + collectionMorePending = emptySet(), + artistAlbumHasMore = emptyMap(), + artistTrackHasMore = emptyMap(), + artistMorePending = emptySet(), artists = emptyMap(), artistAlbums = emptyMap(), artistTracks = emptyMap(), @@ -744,15 +939,22 @@ class SpotifyViewModel( searchJob?.cancel() val query = _state.value.query if (query.trim().length < 2) { - _state.update { it.copy(searchResults = SpotifySearchResults(), searchPending = false) } + _state.update { + it.copy( + searchResults = SpotifySearchResults(), + searchPending = false, + searchHasMore = emptySet(), + searchMorePending = emptySet() + ) + } return } - _state.update { it.copy(searchPending = true) } + _state.update { it.copy(searchPending = true, searchHasMore = emptySet(), searchMorePending = emptySet()) } searchJob = viewModelScope.launch { delay(debounceMs) val scopes = _state.value.searchScopes val results = try { - if (_state.value.signedIn) catalog.search(query, scopes) else SpotifySearchResults() + if (_state.value.signedIn) catalog.search(query, scopes, 0, SPOTIFY_PAGE_SIZE) else SpotifySearchResults() } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -761,7 +963,11 @@ class SpotifyViewModel( } _state.update { if (it.query == query && it.searchScopes == scopes) { - it.copy(searchResults = results, searchPending = false) + it.copy( + searchResults = results, + searchPending = false, + searchHasMore = scopes.filterTo(mutableSetOf()) { scope -> results.sizeFor(scope) >= SPOTIFY_PAGE_SIZE } + ) } else { it } @@ -769,6 +975,57 @@ class SpotifyViewModel( } } + fun loadMoreSearch(scope: SearchScope) { + val snapshot = _state.value + val query = snapshot.query + if (scope !in snapshot.searchHasMore || scope in snapshot.searchMorePending) return + val offset = snapshot.searchResults.sizeFor(scope) + _state.update { it.copy(searchMorePending = it.searchMorePending + scope) } + viewModelScope.launch { + try { + val more = catalog.search(query, setOf(scope), offset, SPOTIFY_PAGE_SIZE) + _state.update { + if (it.query != query) { + it.copy(searchMorePending = it.searchMorePending - scope) + } else { + val hasMore = if (more.sizeFor(scope) >= SPOTIFY_PAGE_SIZE) { + it.searchHasMore + scope + } else { + it.searchHasMore - scope + } + it.copy( + searchResults = it.searchResults.merge(scope, more), + searchHasMore = hasMore, + searchMorePending = it.searchMorePending - scope + ) + } + } + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + _state.update { + it.copy(searchMorePending = it.searchMorePending - scope, notice = playbackNotice(e)) + } + } + } + } + + private fun SpotifySearchResults.sizeFor(scope: SearchScope): Int = when (scope) { + SearchScope.Tracks -> tracks.size + SearchScope.Artists -> artists.size + SearchScope.Albums -> albums.size + SearchScope.Playlists -> playlists.size + SearchScope.Shows -> shows.size + } + + private fun SpotifySearchResults.merge(scope: SearchScope, next: SpotifySearchResults): SpotifySearchResults = when (scope) { + SearchScope.Tracks -> copy(tracks = (tracks + next.tracks).distinctBy { it.id }) + SearchScope.Artists -> copy(artists = (artists + next.artists).distinctBy { it.id }) + SearchScope.Albums -> copy(albums = (albums + next.albums).distinctBy { it.id }) + SearchScope.Playlists -> copy(playlists = (playlists + next.playlists).distinctBy { it.id }) + SearchScope.Shows -> copy(shows = (shows + next.shows).distinctBy { it.id }) + } + /** * librespot is the source of truth while it is playing: its position events keep the seek bar * honest through buffering and seeks, and EndOfTrack advances the Sidekit queue exactly once. @@ -893,4 +1150,13 @@ class SpotifyViewModel( } private fun collectionKey(type: SpotifyCollectionType, id: String) = "${type.route}:$id" + + private fun libraryItemKey(item: Any): String = when (item) { + is SpotifyTrack -> "t${item.id}" + is SpotifyPlaylist -> "p${item.id}" + is SpotifyAlbum -> "a${item.id}" + is SpotifyArtist -> "r${item.id}" + is SpotifyShow -> "s${item.id}" + else -> item.toString() + } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index d25be4d1..8cd190fb 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -43,17 +43,17 @@ object NativeBridge : SpotifyNative { override fun pollEvent(): String? = if (isAvailable) runCatching { nativePollEvent() }.getOrNull() else null - override fun catalogLibrary(filter: String): String? = if (isAvailable) runCatching { nativeCatalogLibrary(filter) }.getOrNull() else null + override fun catalogLibrary(filter: String, offset: Long, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogLibrary(filter, offset, limit) }.getOrNull() else null - override fun catalogCollection(kind: String, id: String): String? = if (isAvailable) runCatching { nativeCatalogCollection(kind, id) }.getOrNull() else null + override fun catalogCollection(kind: String, id: String, offset: Long, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogCollection(kind, id, offset, limit) }.getOrNull() else null override fun catalogArtist(id: String): String? = if (isAvailable) runCatching { nativeCatalogArtist(id) }.getOrNull() else null - override fun catalogAlbumsBy(id: String): String? = if (isAvailable) runCatching { nativeCatalogAlbumsBy(id) }.getOrNull() else null + override fun catalogAlbumsBy(id: String, offset: Long, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogAlbumsBy(id, offset, limit) }.getOrNull() else null - override fun catalogTopTracks(id: String): String? = if (isAvailable) runCatching { nativeCatalogTopTracks(id) }.getOrNull() else null + override fun catalogTopTracks(id: String, offset: Long, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogTopTracks(id, offset, limit) }.getOrNull() else null - override fun catalogSearch(query: String, scopes: String, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogSearch(query, scopes, limit) }.getOrNull() else null + override fun catalogSearch(query: String, scopes: String, offset: Long, limit: Long): String? = if (isAvailable) runCatching { nativeCatalogSearch(query, scopes, offset, limit) }.getOrNull() else null override fun collectionContains(set: String, uris: String): String? = if (isAvailable) runCatching { nativeCollectionContains(set, uris) }.getOrNull() else null @@ -77,12 +77,12 @@ object NativeBridge : SpotifyNative { private external fun nativeReadAudio(timeoutMs: Long): ByteArray? private external fun nativeAudioFormat(): String? private external fun nativePollEvent(): String? - private external fun nativeCatalogLibrary(filter: String): String? - private external fun nativeCatalogCollection(kind: String, id: String): String? + private external fun nativeCatalogLibrary(filter: String, offset: Long, limit: Long): String? + private external fun nativeCatalogCollection(kind: String, id: String, offset: Long, limit: Long): String? private external fun nativeCatalogArtist(id: String): String? - private external fun nativeCatalogAlbumsBy(id: String): String? - private external fun nativeCatalogTopTracks(id: String): String? - private external fun nativeCatalogSearch(query: String, scopes: String, limit: Long): String? + private external fun nativeCatalogAlbumsBy(id: String, offset: Long, limit: Long): String? + private external fun nativeCatalogTopTracks(id: String, offset: Long, limit: Long): String? + private external fun nativeCatalogSearch(query: String, scopes: String, offset: Long, limit: Long): String? private external fun nativeCollectionContains(set: String, uris: String): String? private external fun nativeCollectionWrite(set: String, uris: String, removed: Boolean): Boolean private external fun nativePlaylistSaved(id: String): String? diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt index 0c8e5cdc..bcae390b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -21,12 +21,12 @@ interface SpotifyNative { fun audioFormat(): String? fun pollEvent(): String? - fun catalogLibrary(filter: String): String? - fun catalogCollection(kind: String, id: String): String? + fun catalogLibrary(filter: String, offset: Long, limit: Long): String? + fun catalogCollection(kind: String, id: String, offset: Long, limit: Long): String? fun catalogArtist(id: String): String? - fun catalogAlbumsBy(id: String): String? - fun catalogTopTracks(id: String): String? - fun catalogSearch(query: String, scopes: String, limit: Long): String? + fun catalogAlbumsBy(id: String, offset: Long, limit: Long): String? + fun catalogTopTracks(id: String, offset: Long, limit: Long): String? + fun catalogSearch(query: String, scopes: String, offset: Long, limit: Long): String? fun collectionContains(set: String, uris: String): String? fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt index 39fe80b0..37133ba6 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt @@ -7,6 +7,7 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyArtist import hrln.interactive.sidekit.apps.spotify.SpotifyCollection import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType import hrln.interactive.sidekit.apps.spotify.SpotifyEpisode +import hrln.interactive.sidekit.apps.spotify.SpotifyPage import hrln.interactive.sidekit.apps.spotify.SpotifyPlaylist import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults import hrln.interactive.sidekit.apps.spotify.SpotifyShow @@ -31,19 +32,25 @@ class NativeSpotifyCatalog( /** The protocol has no safe recently-played endpoint; the ViewModel falls back to local history. */ override suspend fun recentlyPlayed(): List = emptyList() - override suspend fun library(filter: LibraryFilter): List { - val array = parseArray(callText { native.catalogLibrary(filter.name) }) - return when (filter) { + override suspend fun library(filter: LibraryFilter, offset: Int, limit: Int): SpotifyPage { + val array = parseArray(callText { native.catalogLibrary(filter.name, offset.toLong(), limit.toLong()) }) + val items = when (filter) { LibraryFilter.LikedSongs -> array.tracks() LibraryFilter.Playlists -> array.playlists() LibraryFilter.Albums -> array.albums() LibraryFilter.Artists -> array.artists() LibraryFilter.Shows -> array.shows() } + return SpotifyPage.of(items, offset, limit) } - override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? { - val json = call { native.catalogCollection(type.name, id) } + override suspend fun collection( + type: SpotifyCollectionType, + id: String, + offset: Int, + limit: Int + ): SpotifyCollection? { + val json = call { native.catalogCollection(type.name, id, offset.toLong(), limit.toLong()) } return runCatching { json.collection(type) }.getOrNull() } @@ -52,19 +59,24 @@ class NativeSpotifyCatalog( return runCatching { artistFromNative(json) }.getOrNull() } - override suspend fun albumsBy(artist: SpotifyArtist): List { - val array = parseArray(callText { native.catalogAlbumsBy(artist.id) }) - return array.albums() + override suspend fun albumsBy(artist: SpotifyArtist, offset: Int, limit: Int): SpotifyPage { + val array = parseArray(callText { native.catalogAlbumsBy(artist.id, offset.toLong(), limit.toLong()) }) + return SpotifyPage.of(array.albums(), offset, limit) } - override suspend fun topTracks(artist: SpotifyArtist): List { - val array = parseArray(callText { native.catalogTopTracks(artist.id) }) - return array.tracks() + override suspend fun topTracks(artist: SpotifyArtist, offset: Int, limit: Int): SpotifyPage { + val array = parseArray(callText { native.catalogTopTracks(artist.id, offset.toLong(), limit.toLong()) }) + return SpotifyPage.of(array.tracks(), offset, limit) } - override suspend fun search(query: String, scopes: Set): SpotifySearchResults { + override suspend fun search( + query: String, + scopes: Set, + offset: Int, + limit: Int + ): SpotifySearchResults { val scopeJson = JSONArray(scopes.map { it.name }).toString() - val json = call { native.catalogSearch(query.trim(), scopeJson, 20L) } + val json = call { native.catalogSearch(query.trim(), scopeJson, offset.toLong(), limit.toLong()) } return SpotifySearchResults( tracks = json.optJSONArray("tracks").tracks(), artists = json.optJSONArray("artists").artists(), @@ -207,7 +219,9 @@ private fun JSONObject.collection(type: SpotifyCollectionType): SpotifyCollectio artRes = 0, items = items, artistId = optString("artistId").takeIf { it.isNotEmpty() }, - artUrl = optString("artUrl").takeIf { it.isNotEmpty() } + artUrl = optString("artUrl").takeIf { it.isNotEmpty() }, + totalItems = optInt("totalItems", items.size), + hasMore = optBoolean("hasMore", false) ) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt index 54a8d390..bc2b34dd 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt @@ -1,11 +1,13 @@ package hrln.interactive.sidekit.apps.spotify.data import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SPOTIFY_PAGE_SIZE import hrln.interactive.sidekit.apps.spotify.SearchScope import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum import hrln.interactive.sidekit.apps.spotify.SpotifyArtist import hrln.interactive.sidekit.apps.spotify.SpotifyCollection import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifyPage import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults import hrln.interactive.sidekit.apps.spotify.SpotifyTrack @@ -15,12 +17,38 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyTrack */ interface SpotifyCatalog { suspend fun recentlyPlayed(): List - suspend fun library(filter: LibraryFilter): List - suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? + suspend fun library( + filter: LibraryFilter, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyPage + + suspend fun collection( + type: SpotifyCollectionType, + id: String, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyCollection? + suspend fun artist(id: String): SpotifyArtist? - suspend fun albumsBy(artist: SpotifyArtist): List - suspend fun topTracks(artist: SpotifyArtist): List - suspend fun search(query: String, scopes: Set): SpotifySearchResults + suspend fun albumsBy( + artist: SpotifyArtist, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyPage + + suspend fun topTracks( + artist: SpotifyArtist, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyPage + + suspend fun search( + query: String, + scopes: Set, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifySearchResults } /** diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt index 5ab5a219..ca984639 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt @@ -1,12 +1,14 @@ package hrln.interactive.sidekit.apps.spotify.data import hrln.interactive.sidekit.apps.spotify.LibraryFilter +import hrln.interactive.sidekit.apps.spotify.SPOTIFY_PAGE_SIZE import hrln.interactive.sidekit.apps.spotify.SearchScope import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum import hrln.interactive.sidekit.apps.spotify.SpotifyArtist import hrln.interactive.sidekit.apps.spotify.SpotifyCollection import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType import hrln.interactive.sidekit.apps.spotify.SpotifyEpisode +import hrln.interactive.sidekit.apps.spotify.SpotifyPage import hrln.interactive.sidekit.apps.spotify.SpotifyPlaylist import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults import hrln.interactive.sidekit.apps.spotify.SpotifyShow @@ -35,21 +37,48 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { } } - suspend fun library(filter: LibraryFilter): List = when (filter) { - LibraryFilter.LikedSongs -> tracks("$ME_TRACKS?limit=50") - LibraryFilter.Playlists -> playlists("$ME_PLAYLISTS?limit=50") - LibraryFilter.Albums -> savedAlbums() - LibraryFilter.Artists -> followedArtists() - LibraryFilter.Shows -> savedShows() + suspend fun library( + filter: LibraryFilter, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyPage { + val page = { items: List, total: Int? -> SpotifyPage.of(items, offset, limit, total) } + return when (filter) { + LibraryFilter.LikedSongs -> { + val json = get("$ME_TRACKS?limit=$limit&offset=$offset", token()) + page(tracks(json), json.optInt("total").takeIf { it > 0 }) + } + + LibraryFilter.Playlists -> { + val json = get("$ME_PLAYLISTS?limit=$limit&offset=$offset", token()) + page(json.optJSONArray("items").playlists(), json.optInt("total").takeIf { it > 0 }) + } + + LibraryFilter.Albums -> savedAlbums(offset, limit) + + LibraryFilter.Artists -> followedArtists(offset, limit) + + LibraryFilter.Shows -> savedShows(offset, limit) + } } - suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { - SpotifyCollectionType.Album -> runCatching { get("$ALBUMS/${enc(id)}", token()) }.getOrNull()?.let(::albumCollection) - SpotifyCollectionType.Playlist -> runCatching { get("$PLAYLISTS/${enc(id)}", token()) }.getOrNull()?.let(::playlistCollection) - SpotifyCollectionType.Show -> runCatching { get("$SHOWS/${enc(id)}", token()) }.getOrNull()?.let(::showCollection) + suspend fun collection( + type: SpotifyCollectionType, + id: String, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyCollection? = when (type) { + SpotifyCollectionType.Album -> runCatching { albumCollection(id, offset, limit) }.getOrNull() + SpotifyCollectionType.Playlist -> runCatching { playlistCollection(id, offset, limit) }.getOrNull() + SpotifyCollectionType.Show -> runCatching { showCollection(id, offset, limit) }.getOrNull() } - suspend fun search(query: String, scopes: Set, limit: Int = 20): SpotifySearchResults { + suspend fun search( + query: String, + scopes: Set, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifySearchResults { if (query.trim().length < 2) return SpotifySearchResults() val types = buildList { if (SearchScope.Tracks in scopes) add("track") @@ -60,7 +89,7 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { } if (types.isEmpty()) return SpotifySearchResults() val json = get( - "$SEARCH?q=${enc(query.trim())}&type=${types.joinToString(",")}&limit=${limit.coerceIn(1, 50)}&market=from_token", + "$SEARCH?q=${enc(query.trim())}&type=${types.joinToString(",")}&limit=${limit.coerceIn(1, 50)}&offset=$offset&market=from_token", token() ) return SpotifySearchResults( @@ -76,21 +105,34 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { artist(get("$ARTISTS/${enc(id)}", token())) }.getOrNull() - suspend fun albumsBy(artist: SpotifyArtist): List = runCatching { - val json = get("$ARTISTS/${enc(artist.id)}/albums?include_groups=album,single&limit=50&market=from_token", token()) - json.optJSONArray("items").albums() - }.getOrDefault(emptyList()) + suspend fun albumsBy( + artist: SpotifyArtist, + offset: Int = 0, + limit: Int = SPOTIFY_PAGE_SIZE + ): SpotifyPage { + val json = runCatching { + get( + "$ARTISTS/${enc(artist.id)}/albums?include_groups=album,single&limit=$limit&offset=$offset&market=from_token", + token() + ) + }.getOrNull() + val albums = json?.optJSONArray("items").albums().orEmpty() + return SpotifyPage.of(albums, offset, limit, json?.optInt("total")?.takeIf { it > 0 }) + } - suspend fun topTracks(artist: SpotifyArtist): List = runCatching { - val json = get("$ARTISTS/${enc(artist.id)}/top-tracks?market=from_token", token()) - json.optJSONArray("tracks").tracks() - }.getOrDefault(emptyList()) + suspend fun topTracks(artist: SpotifyArtist, offset: Int = 0, limit: Int = SPOTIFY_PAGE_SIZE): SpotifyPage { + val json = runCatching { + get("$ARTISTS/${enc(artist.id)}/top-tracks?market=from_token", token()) + }.getOrNull() + val tracks = json?.optJSONArray("tracks").tracks().orEmpty().drop(offset).take(limit) + return SpotifyPage.of(tracks, offset, limit) + } /** Visible for tests: maps Spotify's search envelope without touching the network. */ internal fun parseSearchTracks(json: JSONObject): List = json.optJSONObject("tracks")?.optJSONArray("items").tracks() - private suspend fun tracks(url: String): List { - val items = get(url, token()).optJSONArray("items") ?: return emptyList() + private fun tracks(json: JSONObject): List { + val items = json.optJSONArray("items") ?: return emptyList() return buildList(items.length()) { for (i in 0 until items.length()) { items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) @@ -98,34 +140,40 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { } } - private suspend fun playlists(url: String): List = get(url, token()).optJSONArray("items").playlists() - - private suspend fun savedAlbums(): List { - val items = get("$ME_ALBUMS?limit=50", token()).optJSONArray("items") ?: return emptyList() - return buildList(items.length()) { + private suspend fun savedAlbums(offset: Int, limit: Int): SpotifyPage { + val json = get("$ME_ALBUMS?limit=$limit&offset=$offset", token()) + val items = json.optJSONArray("items") ?: JSONArray() + val albums = buildList(items.length()) { for (i in 0 until items.length()) { items.optJSONObject(i)?.optJSONObject("album")?.let(::album)?.let(::add) } } + return SpotifyPage.of(albums, offset, limit, json.optInt("total").takeIf { it > 0 }) } - private suspend fun followedArtists(): List { - val json = get("$ME_FOLLOWING?type=artist&limit=50", token()) - return json.optJSONObject("artists")?.optJSONArray("items").artists() + private suspend fun followedArtists(offset: Int, limit: Int): SpotifyPage { + val json = get("$ME_FOLLOWING?type=artist&limit=$limit&offset=$offset", token()) + val page = json.optJSONObject("artists") + val artists = page?.optJSONArray("items").artists().orEmpty() + return SpotifyPage.of(artists, offset, limit, page?.optInt("total")?.takeIf { it > 0 }) } - private suspend fun savedShows(): List { - val items = get("$ME_SHOWS?limit=50", token()).optJSONArray("items") ?: return emptyList() - return buildList(items.length()) { + private suspend fun savedShows(offset: Int, limit: Int): SpotifyPage { + val json = get("$ME_SHOWS?limit=$limit&offset=$offset", token()) + val items = json.optJSONArray("items") ?: JSONArray() + val shows = buildList(items.length()) { for (i in 0 until items.length()) { items.optJSONObject(i)?.optJSONObject("show")?.let(::show)?.let(::add) } } + return SpotifyPage.of(shows, offset, limit, json.optInt("total").takeIf { it > 0 }) } - private fun albumCollection(json: JSONObject): SpotifyCollection? { + private suspend fun albumCollection(id: String, offset: Int, limit: Int): SpotifyCollection? { + val json = get("$ALBUMS/${enc(id)}", token()) val album = album(json) ?: return null - val tracks = json.optJSONObject("tracks")?.optJSONArray("items").tracks() + val page = get("$ALBUMS/${enc(id)}/tracks?limit=$limit&offset=$offset", token()) + val tracks = page.optJSONArray("items").tracks() return SpotifyCollection( type = SpotifyCollectionType.Album, id = album.id, @@ -134,18 +182,17 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { artRes = 0, items = tracks, artistId = album.artistId, - artUrl = album.artUrl + artUrl = album.artUrl, + totalItems = album.trackCount, + hasMore = offset + tracks.size < album.trackCount ) } - private fun playlistCollection(json: JSONObject): SpotifyCollection? { + private suspend fun playlistCollection(id: String, offset: Int, limit: Int): SpotifyCollection? { + val json = get("$PLAYLISTS/${enc(id)}", token()) val playlist = playlist(json) ?: return null - val items = json.optJSONObject("tracks")?.optJSONArray("items") ?: JSONArray() - val tracks = buildList(items.length()) { - for (i in 0 until items.length()) { - items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) - } - } + val page = get("$PLAYLISTS/${enc(id)}/tracks?limit=$limit&offset=$offset&market=from_token", token()) + val tracks = tracks(page) return SpotifyCollection( type = SpotifyCollectionType.Playlist, id = playlist.id, @@ -154,13 +201,17 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { artRes = 0, items = tracks, artistId = playlist.ownerId, - artUrl = playlist.artUrl + artUrl = playlist.artUrl, + totalItems = playlist.trackCount, + hasMore = offset + tracks.size < playlist.trackCount ) } - private fun showCollection(json: JSONObject): SpotifyCollection? { + private suspend fun showCollection(id: String, offset: Int, limit: Int): SpotifyCollection? { + val json = get("$SHOWS/${enc(id)}", token()) val show = show(json) ?: return null - val items = json.optJSONObject("episodes")?.optJSONArray("items") ?: JSONArray() + val page = get("$SHOWS/${enc(id)}/episodes?limit=$limit&offset=$offset&market=from_token", token()) + val items = page.optJSONArray("items") ?: JSONArray() val episodes = buildList(items.length()) { for (i in 0 until items.length()) { items.optJSONObject(i)?.let { episode(it, show) }?.let(::add) @@ -173,7 +224,9 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { subtitle = "${show.publisher} · ${show.episodeCount} episodes", artRes = 0, items = episodes, - artUrl = show.artUrl + artUrl = show.artUrl, + totalItems = show.episodeCount, + hasMore = offset + episodes.size < show.episodeCount ) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt index 79105f62..c9a893da 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt @@ -6,6 +6,7 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyAlbum import hrln.interactive.sidekit.apps.spotify.SpotifyArtist import hrln.interactive.sidekit.apps.spotify.SpotifyCollection import hrln.interactive.sidekit.apps.spotify.SpotifyCollectionType +import hrln.interactive.sidekit.apps.spotify.SpotifyPage import hrln.interactive.sidekit.apps.spotify.SpotifySearchResults import hrln.interactive.sidekit.apps.spotify.SpotifyTrack @@ -13,15 +14,37 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyTrack class SpotifyWebCatalog(private val api: SpotifyWebApi) : SpotifyCatalog { override suspend fun recentlyPlayed(): List = api.recentlyPlayed() - override suspend fun library(filter: LibraryFilter): List = api.library(filter) + override suspend fun library( + filter: LibraryFilter, + offset: Int, + limit: Int + ): SpotifyPage = api.library(filter, offset, limit) - override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = api.collection(type, id) + override suspend fun collection( + type: SpotifyCollectionType, + id: String, + offset: Int, + limit: Int + ): SpotifyCollection? = api.collection(type, id, offset, limit) override suspend fun artist(id: String): SpotifyArtist? = api.artist(id) - override suspend fun albumsBy(artist: SpotifyArtist): List = api.albumsBy(artist) - - override suspend fun topTracks(artist: SpotifyArtist): List = api.topTracks(artist) - - override suspend fun search(query: String, scopes: Set): SpotifySearchResults = api.search(query, scopes) + override suspend fun albumsBy( + artist: SpotifyArtist, + offset: Int, + limit: Int + ): SpotifyPage = api.albumsBy(artist, offset, limit) + + override suspend fun topTracks( + artist: SpotifyArtist, + offset: Int, + limit: Int + ): SpotifyPage = api.topTracks(artist, offset, limit) + + override suspend fun search( + query: String, + scopes: Set, + offset: Int, + limit: Int + ): SpotifySearchResults = api.search(query, scopes, offset, limit) } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt index e4fb1be5..cb1bd639 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt @@ -187,45 +187,83 @@ object MockSpotifyDataSource : SpotifyCatalog { override suspend fun artist(id: String): SpotifyArtist? = artists.firstOrNull { it.id == id } fun track(id: String): SpotifyTrack? = albums.flatMap { it.tracks }.firstOrNull { it.id == id } - override suspend fun collection(type: SpotifyCollectionType, id: String): SpotifyCollection? = when (type) { + override suspend fun collection( + type: SpotifyCollectionType, + id: String, + offset: Int, + limit: Int + ): SpotifyCollection? = when (type) { SpotifyCollectionType.Album -> album(id)?.let { - SpotifyCollection(type, id, it.title, "${it.artist} · ${it.year} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.artistId) + val items = it.tracks.drop(offset).take(limit) + SpotifyCollection( + type, id, it.title, "${it.artist} · ${it.year} · ${it.tracks.size} tracks", it.artRes, items, it.artistId, + totalItems = it.tracks.size, hasMore = offset + items.size < it.tracks.size + ) } SpotifyCollectionType.Playlist -> playlist(id)?.let { - SpotifyCollection(type, id, it.name, "${it.owner} · ${it.tracks.size} tracks", it.artRes, it.tracks, it.ownerId) + val items = it.tracks.drop(offset).take(limit) + SpotifyCollection( + type, id, it.name, "${it.owner} · ${it.tracks.size} tracks", it.artRes, items, it.ownerId, + totalItems = it.tracks.size, hasMore = offset + items.size < it.tracks.size + ) } SpotifyCollectionType.Show -> show(id)?.let { - SpotifyCollection(type, id, it.name, "${it.publisher} · ${it.episodes.size} episodes", it.artRes, it.episodes) + val items = it.episodes.drop(offset).take(limit) + SpotifyCollection( + type, + id, + it.name, + "${it.publisher} · ${it.episodes.size} episodes", + it.artRes, + items, + totalItems = it.episodes.size, + hasMore = offset + items.size < it.episodes.size + ) } } - override suspend fun topTracks(artist: SpotifyArtist): List = topTracks(artist, 10) + override suspend fun topTracks(artist: SpotifyArtist, offset: Int, limit: Int): SpotifyPage { + val all = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks } + return SpotifyPage.of(all.drop(offset).take(limit), offset, limit, all.size) + } fun topTracks(artist: SpotifyArtist, limit: Int): List = albums.filter { it.id in artist.albumIds }.flatMap { it.tracks }.take(limit) - override suspend fun albumsBy(artist: SpotifyArtist): List = artist.albumIds.mapNotNull(::album) + override suspend fun albumsBy(artist: SpotifyArtist, offset: Int, limit: Int): SpotifyPage { + val all = artist.albumIds.mapNotNull(::album) + return SpotifyPage.of(all.drop(offset).take(limit), offset, limit, all.size) + } - override suspend fun library(filter: LibraryFilter): List = when (filter) { - LibraryFilter.LikedSongs -> liked - LibraryFilter.Playlists -> playlists - LibraryFilter.Albums -> albums - LibraryFilter.Artists -> artists - LibraryFilter.Shows -> shows + override suspend fun library(filter: LibraryFilter, offset: Int, limit: Int): SpotifyPage { + val all: List = when (filter) { + LibraryFilter.LikedSongs -> liked + LibraryFilter.Playlists -> playlists + LibraryFilter.Albums -> albums + LibraryFilter.Artists -> artists + LibraryFilter.Shows -> shows + } + return SpotifyPage.of(all.drop(offset).take(limit), offset, limit, all.size) } - override suspend fun search(query: String, scopes: Set): SpotifySearchResults { + override suspend fun search( + query: String, + scopes: Set, + offset: Int, + limit: Int + ): SpotifySearchResults { val q = query.trim() if (q.length < 2) return SpotifySearchResults() fun match(vararg text: String) = text.any { it.contains(q, ignoreCase = true) } val allTracks = albums.flatMap { it.tracks } + fun page(items: List): List = items.drop(offset).take(limit) return SpotifySearchResults( - tracks = if (SearchScope.Tracks in scopes) allTracks.filter { match(it.title, it.artist) } else emptyList(), - artists = if (SearchScope.Artists in scopes) artists.filter { match(it.name) } else emptyList(), - albums = if (SearchScope.Albums in scopes) albums.filter { match(it.title, it.artist) } else emptyList(), - playlists = if (SearchScope.Playlists in scopes) playlists.filter { match(it.name, it.owner) } else emptyList(), - shows = if (SearchScope.Shows in scopes) shows.filter { match(it.name, it.publisher) } else emptyList() + tracks = if (SearchScope.Tracks in scopes) page(allTracks.filter { match(it.title, it.artist) }) else emptyList(), + artists = if (SearchScope.Artists in scopes) page(artists.filter { match(it.name) }) else emptyList(), + albums = if (SearchScope.Albums in scopes) page(albums.filter { match(it.title, it.artist) }) else emptyList(), + playlists = if (SearchScope.Playlists in scopes) page(playlists.filter { match(it.name, it.owner) }) else emptyList(), + shows = if (SearchScope.Shows in scopes) page(shows.filter { match(it.name, it.publisher) }) else emptyList() ) } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt index 4f854e78..d02eebce 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt @@ -6,6 +6,8 @@ import hrln.interactive.sidekit.apps.spotify.data.NativeSpotifyCatalog import hrln.interactive.sidekit.apps.spotify.data.SpotifyFailure import hrln.interactive.sidekit.apps.spotify.data.SpotifyGatewayException import kotlinx.coroutines.test.runTest +import org.json.JSONArray +import org.json.JSONObject import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -59,6 +61,52 @@ class NativeSpotifyCatalogTest { assertTrue(catalog.isSaved(SpotifyCollectionType.Playlist, "playlist1") == true) } + @Test fun `library forwards paging and reports another page`() = runTest { + val tracks = JSONArray().apply { + repeat(50) { put(JSONObject().put("id", "t$it")) } + } + val native = FakeNative().apply { libraryResult = tracks.toString() } + val catalog = NativeSpotifyCatalog(native) {} + + val page = catalog.library(LibraryFilter.LikedSongs, offset = 50, limit = 50) + + assertEquals(50, page.items.size) + assertTrue(page.hasMore) + assertEquals(50L, native.libraryOffset) + assertEquals(50L, native.libraryLimit) + } + + @Test fun `collection preserves total and page state`() = runTest { + val native = FakeNative().apply { + collectionResult = JSONObject() + .put("id", "p1") + .put("title", "Long playlist") + .put("subtitle", "You · 202 tracks") + .put("items", JSONArray()) + .put("totalItems", 202) + .put("hasMore", true) + .toString() + } + val catalog = NativeSpotifyCatalog(native) {} + + val collection = catalog.collection(SpotifyCollectionType.Playlist, "p1", offset = 50, limit = 50) + + assertEquals(202, collection?.totalItems) + assertTrue(collection?.hasMore == true) + assertEquals(50L, native.collectionOffset) + assertEquals(50L, native.collectionLimit) + } + + @Test fun `search forwards its offset and limit`() = runTest { + val native = FakeNative().apply { searchResult = "{\"tracks\":[]}" } + val catalog = NativeSpotifyCatalog(native) {} + + catalog.search("night", setOf(SearchScope.Tracks), offset = 50, limit = 50) + + assertEquals(50L, native.searchOffset) + assertEquals(50L, native.searchLimit) + } + @Test fun `failed write surfaces a network failure`() = runTest { val native = FakeNative().apply { writeOk = false } val catalog = NativeSpotifyCatalog(native) {} @@ -79,6 +127,15 @@ class NativeSpotifyCatalogTest { var playlistSaved = "false" var playlistId: String? = null var playlistSave = false + var libraryResult = "[]" + var libraryOffset = -1L + var libraryLimit = -1L + var collectionResult = "{}" + var collectionOffset = -1L + var collectionLimit = -1L + var searchResult = "{}" + var searchOffset = -1L + var searchLimit = -1L override fun initialize() = true override fun setTmpDir(path: String) = Unit @@ -94,12 +151,24 @@ class NativeSpotifyCatalogTest { override fun readAudio(timeoutMs: Long): ByteArray? = null override fun audioFormat(): String? = null override fun pollEvent(): String? = null - override fun catalogLibrary(filter: String): String? = null - override fun catalogCollection(kind: String, id: String): String? = null + override fun catalogLibrary(filter: String, offset: Long, limit: Long): String { + libraryOffset = offset + libraryLimit = limit + return libraryResult + } + override fun catalogCollection(kind: String, id: String, offset: Long, limit: Long): String { + collectionOffset = offset + collectionLimit = limit + return collectionResult + } override fun catalogArtist(id: String): String? = null - override fun catalogAlbumsBy(id: String): String? = null - override fun catalogTopTracks(id: String): String? = null - override fun catalogSearch(query: String, scopes: String, limit: Long): String? = null + override fun catalogAlbumsBy(id: String, offset: Long, limit: Long): String? = null + override fun catalogTopTracks(id: String, offset: Long, limit: Long): String? = null + override fun catalogSearch(query: String, scopes: String, offset: Long, limit: Long): String { + searchOffset = offset + searchLimit = limit + return searchResult + } override fun collectionContains(set: String, uris: String): String? = contains override fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean { this.set = set diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt index 82b3ecbf..b62c50d4 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -119,12 +119,12 @@ class NativeSpotifyGatewayTest { override fun readAudio(timeoutMs: Long): ByteArray? = null override fun audioFormat(): String? = null override fun pollEvent(): String? = null - override fun catalogLibrary(filter: String): String? = null - override fun catalogCollection(kind: String, id: String): String? = null + override fun catalogLibrary(filter: String, offset: Long, limit: Long): String? = null + override fun catalogCollection(kind: String, id: String, offset: Long, limit: Long): String? = null override fun catalogArtist(id: String): String? = null - override fun catalogAlbumsBy(id: String): String? = null - override fun catalogTopTracks(id: String): String? = null - override fun catalogSearch(query: String, scopes: String, limit: Long): String? = null + override fun catalogAlbumsBy(id: String, offset: Long, limit: Long): String? = null + override fun catalogTopTracks(id: String, offset: Long, limit: Long): String? = null + override fun catalogSearch(query: String, scopes: String, offset: Long, limit: Long): String? = null override fun collectionContains(set: String, uris: String): String? = null override fun collectionWrite(set: String, uris: String, removed: Boolean): Boolean = true override fun playlistSaved(id: String): String? = null diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt index de0a9929..730a49ea 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt @@ -29,7 +29,7 @@ class SidetracksScreenshotTest { upcoming = recent.drop(1), history = recent.drop(3), recent = recent, - libraryItems = runBlocking { MockSpotifyDataSource.library(LibraryFilter.Albums) }, + libraryItems = runBlocking { MockSpotifyDataSource.library(LibraryFilter.Albums).items }, searchResults = runBlocking { MockSpotifyDataSource.search("night", SearchScope.entries.toSet()) }, isPlaying = true, positionMs = 68_000L, @@ -64,6 +64,7 @@ class SidetracksScreenshotTest { onAlbum = {}, onArtist = {}, onShow = {}, + onLoadMore = {}, onDismissNotice = {} ) } @@ -81,6 +82,8 @@ class SidetracksScreenshotTest { onQueueItem = {}, onNowPlaying = {}, onUndo = {}, + onLoadMore = {}, + loadingMore = false, onDismissNotice = {} ) } @@ -89,8 +92,8 @@ class SidetracksScreenshotTest { val artist = MockSpotifyDataSource.artists.first() ArtistScreen( artist, - runBlocking { MockSpotifyDataSource.albumsBy(artist) }, - runBlocking { MockSpotifyDataSource.topTracks(artist) }, + runBlocking { MockSpotifyDataSource.albumsBy(artist).items }, + runBlocking { MockSpotifyDataSource.topTracks(artist).items }, state, onBack = {}, onPlay = {}, @@ -100,6 +103,11 @@ class SidetracksScreenshotTest { onFollow = {}, onNowPlaying = {}, onUndo = {}, + onLoadMoreAlbums = {}, + onLoadMoreTracks = {}, + albumHasMore = false, + trackHasMore = false, + loadingMore = false, onDismissNotice = {} ) } @@ -117,6 +125,7 @@ class SidetracksScreenshotTest { onAlbum = {}, onPlaylist = {}, onShow = {}, + onLoadMore = {}, onDismissNotice = {} ) } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPaginationTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPaginationTest.kt new file mode 100644 index 00000000..cef229e5 --- /dev/null +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPaginationTest.kt @@ -0,0 +1,52 @@ +package hrln.interactive.sidekit.apps.spotify + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SpotifyPaginationTest { + @Test fun `collection pages continue without gaps`() = runTest { + val full = MockSpotifyDataSource.playlist("late_night")!!.tracks + val first = MockSpotifyDataSource.collection(SpotifyCollectionType.Playlist, "late_night", 0, 3)!! + val second = MockSpotifyDataSource.collection(SpotifyCollectionType.Playlist, "late_night", 3, 3)!! + + assertEquals(full.take(3), first.items) + assertEquals(full.drop(3).take(3), second.items) + assertEquals(full.size, first.totalItems) + assertTrue(first.hasMore) + } + + @Test fun `last collection page reports no more`() = runTest { + val full = MockSpotifyDataSource.playlist("late_night")!!.tracks + val last = MockSpotifyDataSource.collection( + SpotifyCollectionType.Playlist, + "late_night", + full.size - 2, + 3 + )!! + + assertEquals(full.takeLast(2), last.items) + assertFalse(last.hasMore) + } + + @Test fun `library page reports total and more`() = runTest { + val page = MockSpotifyDataSource.library(LibraryFilter.LikedSongs, 0, 4) + + assertEquals(4, page.items.size) + assertEquals(MockSpotifyDataSource.liked.size, page.total) + assertTrue(page.hasMore) + } + + @Test fun `search offset advances each section`() = runTest { + val first = MockSpotifyDataSource.search("night", SearchScope.entries.toSet(), 0, 2) + val second = MockSpotifyDataSource.search("night", SearchScope.entries.toSet(), 2, 2) + + assertTrue(first.tracks.isNotEmpty()) + assertTrue((first.tracks + second.tracks).distinctBy { it.id }.size >= first.tracks.size) + } +} diff --git a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt index 2ebaae3e..a143827b 100644 --- a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt @@ -1,6 +1,7 @@ package hrln.interactive.sidekit.ui import android.view.KeyEvent +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasContentDescription import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule @@ -16,6 +17,7 @@ import org.junit.Test import org.junit.runner.RunWith /** The keypad's top keys: top-right (ENDCALL) is Options, top-left (CALL) is Back. */ +@OptIn(ExperimentalTestApi::class) @RunWith(AndroidJUnit4::class) class KeypadKeysTest { @get:Rule(order = 0) @@ -28,6 +30,7 @@ class KeypadKeysTest { listLayout = false hiddenApps = emptySet() } + hrln.interactive.sidekit.home.HomePreferences(context).reset() } } @@ -70,6 +73,7 @@ class KeypadKeysTest { @Test fun screensWithoutOptionsSaySo() { // The dial pad has no options until a number is entered. + compose.waitUntilAtLeastOneExists(hasContentDescription("Phonebook"), 5_000) compose.onAllNodes(hasContentDescription("Phonebook")).onLast().performClick() compose.onNodeWithContentDescription("Dial pad").performClick() press(KeyEvent.KEYCODE_MENU) diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDETRACKS.md index f678990b..3a440330 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDETRACKS.md @@ -88,6 +88,9 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac Upcoming and History rows in the queue swipe left to remove; removing queue history also drops the item from recently played. Library uses the standard single-line header; Settings has one sign-out action in the bar. +- Catalog lists page in 50-row chunks. Library, collection, artist, and search surfaces load the + next page as their footer becomes visible, so a 202-track playlist reaches its last track without + composing every row at once. **Verified** @@ -97,6 +100,7 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac playback advances/pauses correctly, show episodes format dates, and artwork no longer crashes. - On `SP01GE260600886`: instrumented tests cover the foreground-service notification and actions, media-session metadata/state, media-owner hand-off, and the headphone-unplug pause path. +- JVM tests cover catalog page offsets, collection totals, and page boundaries. --- @@ -108,8 +112,6 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac and repeat. Queue, queue history, and recently played persist; playback does not resume automatically after process death. 3. **Playlist owner names.** Followed playlists show the owner's Spotify username. -4. **Hard read limits.** Collection 500, playlist tracks 100, show episodes 50, artist albums 50, - search 20. These truncate silently today. --- @@ -129,23 +131,18 @@ cross-cutting refactor. ### P1 — parity and completeness -1. **Pagination.** - - Page liked songs, playlists, albums, artists, shows, playlist tracks, show episodes, artist - albums, and search. - - Remove silent truncation or surface "showing N of M". - - Acceptance: a 202-track playlist scrolls to its last track. -2. **Recently played.** +1. **Recently played.** - Choose a supported first-party history endpoint or keep local history only. - If the Web API is the only source, make it opt-in and degrade to local history when the quota is exhausted. -3. **Queue/shuffle/repeat ownership.** +2. **Queue/shuffle/repeat ownership.** - Decide whether librespot player state or `SpotifyQueueRepository` is authoritative. - If Sidekit stays authoritative, mirror shuffle/repeat/autoplay into the native player and add an explicit native "Play next" path; otherwise delete the duplicate local state. - Acceptance: Now Playing matches what the native player is doing. -4. **Playlist owner names.** Resolve through the authenticated user profile and cache, not on +3. **Playlist owner names.** Resolve through the authenticated user profile and cache, not on every collection open. -5. **Test coverage.** +4. **Test coverage.** - `NativeSpotifyCatalog` JSON parsing and error mapping (auth, network, malformed response). - `NativeSpotifyCatalog`/`SpotifyViewModel` event state machine, including `stopped`. - Device smoke checklist for sign-in, library filters, search, playback, queue, lock screen, diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs index 2199b400..aa17c7f8 100644 --- a/native/sidekit-spotify/src/catalog.rs +++ b/native/sidekit-spotify/src/catalog.rs @@ -30,13 +30,9 @@ use std::time::{SystemTime, UNIX_EPOCH}; const PATHFINDER_BASE: &str = "https://api-partner.spotify.com"; const SEARCH_QUERY_HASH: &str = "d9f785900f0710b31c07818d617f4f7600c1e21217e80f5b043d1e78d74e6026"; -const COLLECTION_LIMIT: usize = 500; const COLLECTION_PAGE: i32 = 100; -const COLLECTION_PAGES: usize = 8; -const PLAYLIST_TRACK_LIMIT: usize = 100; -const SHOW_EPISODE_LIMIT: usize = 50; -const ARTIST_ALBUM_LIMIT: usize = 50; -const ARTIST_TOP_LIMIT: usize = 20; +const COLLECTION_PAGES: usize = 100; +const MAX_PAGE: usize = 200; #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] @@ -131,6 +127,8 @@ struct CollectionJson { items: Vec, artist_id: Option, art_url: Option, + total_items: i32, + has_more: bool, } #[derive(Debug, Serialize)] @@ -703,11 +701,12 @@ async fn pathfinder_search( session: &Session, token: &str, query: &str, + offset: i32, limit: i32, ) -> Result { let variables = json!({ "searchTerm": query, - "offset": 0, + "offset": offset, "limit": limit, "numberOfTopResults": 5, "includeAudiobooks": false, @@ -747,13 +746,14 @@ pub async fn search( session: &Session, token: &str, query: &str, + offset: i32, limit: i32, scopes: &[String], ) -> Result { let want = |scope: &str| { scopes.is_empty() || scopes.iter().any(|value| value.eq_ignore_ascii_case(scope)) }; - match pathfinder_search(session, token, query, limit.clamp(1, 50)).await { + match pathfinder_search(session, token, query, offset.max(0), limit.clamp(1, 50)).await { Ok(value) => { let tracks = if want("Tracks") { section_items(&value, "tracksV2") @@ -825,7 +825,12 @@ pub async fn search( } } } - let tracks = fetch_tracks(session, token, &uris, limit.clamp(1, 50) as usize).await; + let page: Vec = uris + .into_iter() + .skip(offset.max(0) as usize) + .take(limit.clamp(1, 50) as usize) + .collect(); + let tracks = fetch_tracks(session, token, &page, page.len()).await; let results = SearchJson { tracks, artists: Vec::new(), @@ -990,10 +995,12 @@ async fn collection_page( session: &Session, token: &str, set: &str, + offset: usize, limit: usize, ) -> Result, String> { let username = session.username(); let mut items = Vec::new(); + let mut skipped = 0usize; let mut page_token = String::new(); let mut seen = HashSet::new(); for _ in 0..COLLECTION_PAGES { @@ -1016,6 +1023,10 @@ async fn collection_page( let (page_items, next) = parse_collection_page(&bytes); let got = page_items.len(); for item in page_items.into_iter().filter(|item| !item.is_removed) { + if skipped < offset { + skipped += 1; + continue; + } items.push(item); if items.len() >= limit { return Ok(items); @@ -1040,9 +1051,11 @@ fn collection_uris(items: &[CollectionItem], prefix: &str) -> Vec { async fn rootlist_playlists( session: &Session, token: &str, + offset: usize, limit: usize, ) -> Result, String> { let mut seen = 0usize; + let mut skipped = 0usize; let mut playlists = Vec::new(); for _ in 0..COLLECTION_PAGES { let user = session.username(); @@ -1073,13 +1086,21 @@ async fn rootlist_playlists( if name.trim().is_empty() { continue; } + if skipped < offset { + skipped += 1; + continue; + } let owner = metadata .map(|m| m.owner_username().to_string()) .unwrap_or_default(); let art_url = attributes .and_then(|a| a.picture_size.iter().find(|size| !size.url().is_empty())) .map(|size| size.url().to_string()); - let owner_id = if owner.is_empty() { None } else { Some(owner.clone()) }; + let owner_id = if owner.is_empty() { + None + } else { + Some(owner.clone()) + }; let owner = if owner.is_empty() { "Spotify".to_string() } else if owner == user { @@ -1109,7 +1130,6 @@ async fn rootlist_playlists( Ok(playlists) } - fn collection_item_bytes(uri: &str, added_at: i32, removed: bool) -> Vec { let mut item = Vec::new(); put_string(&mut item, 1, uri); @@ -1305,28 +1325,35 @@ pub async fn playlist_save( Ok(()) } -pub async fn library(session: &Session, token: &str, filter: &str) -> Result { +pub async fn library( + session: &Session, + token: &str, + filter: &str, + offset: usize, + limit: usize, +) -> Result { + let limit = limit.clamp(1, MAX_PAGE); match filter { "LikedSongs" => { - let items = collection_page(session, token, "collection", 50).await?; + let items = collection_page(session, token, "collection", offset, limit).await?; let uris = collection_uris(&items, "spotify:track:"); - let tracks = fetch_tracks(session, token, &uris, 50).await; + let tracks = fetch_tracks(session, token, &uris, uris.len()).await; Ok(serde_json::to_string(&tracks).map_err(|e| e.to_string())?) } - "Playlists" => Ok( - serde_json::to_string(&rootlist_playlists(session, token, 200).await?) - .map_err(|e| e.to_string())?, - ), + "Playlists" => Ok(serde_json::to_string( + &rootlist_playlists(session, token, offset, limit).await?, + ) + .map_err(|e| e.to_string())?), "Albums" => { - let items = collection_page(session, token, "collection", COLLECTION_LIMIT).await?; + let items = collection_page(session, token, "collection", offset, limit).await?; let uris = collection_uris(&items, "spotify:album:"); let albums = fetch_albums(session, token, &uris, false).await; Ok(serde_json::to_string(&albums).map_err(|e| e.to_string())?) } "Artists" => { - let mut items = collection_page(session, token, "artist", COLLECTION_LIMIT).await?; + let mut items = collection_page(session, token, "artist", offset, limit).await?; if items.is_empty() { - items = collection_page(session, token, "collection", COLLECTION_LIMIT) + items = collection_page(session, token, "collection", offset, limit) .await? .into_iter() .filter(|item| item.uri.starts_with("spotify:artist:")) @@ -1347,7 +1374,7 @@ pub async fn library(session: &Session, token: &str, filter: &str) -> Result { - let items = collection_page(session, token, "show", COLLECTION_LIMIT).await?; + let items = collection_page(session, token, "show", offset, limit).await?; let uris = collection_uris(&items, "spotify:show:"); let futures = uris.iter().map(|uri| { let session = session.clone(); @@ -1371,14 +1398,19 @@ pub async fn collection( token: &str, kind: &str, id: &str, + offset: usize, + limit: usize, ) -> Result { + let limit = limit.clamp(1, MAX_PAGE); match kind { "Album" => { let album = album_get(session, token, &spotify_uri(id, "album")?) .await .map_err(|e| e.to_string())?; - let uris: Vec = album.tracks().cloned().collect(); - let tracks = fetch_tracks(session, token, &uris, 100).await; + let all_uris: Vec = album.tracks().cloned().collect(); + let total = all_uris.len(); + let uris: Vec = all_uris.into_iter().skip(offset).take(limit).collect(); + let tracks = fetch_tracks(session, token, &uris, uris.len()).await; let album_json = album_json(&album, tracks); let subtitle = format!( "{} · {} · {} tracks", @@ -1397,6 +1429,8 @@ pub async fn collection( .collect(), artist_id: Some(album_json.artist_id.clone()).filter(|value| !value.is_empty()), art_url: album_json.art_url.clone(), + total_items: total as i32, + has_more: offset + album_json.tracks.len() < total, }; Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) } @@ -1404,8 +1438,10 @@ pub async fn collection( let playlist = playlist_get(session, token, &spotify_uri(id, "playlist")?) .await .map_err(|e| e.to_string())?; - let uris: Vec = playlist.tracks().cloned().collect(); - let tracks = fetch_tracks(session, token, &uris, PLAYLIST_TRACK_LIMIT).await; + let all_uris: Vec = playlist.tracks().cloned().collect(); + let total = (playlist.length.max(0) as usize).max(all_uris.len()); + let uris: Vec = all_uris.into_iter().skip(offset).take(limit).collect(); + let tracks = fetch_tracks(session, token, &uris, uris.len()).await; let mut playlist_json = playlist_json(&playlist, tracks); if playlist_json.owner == session.username() { playlist_json.owner = "You".to_string(); @@ -1427,6 +1463,8 @@ pub async fn collection( .collect(), artist_id: playlist_json.owner_id.clone(), art_url: playlist_json.art_url.clone(), + total_items: total as i32, + has_more: offset + playlist_json.tracks.len() < total, }; Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) } @@ -1434,8 +1472,10 @@ pub async fn collection( let show = show_get(session, token, &spotify_uri(id, "show")?) .await .map_err(|e| e.to_string())?; - let uris: Vec = show.episodes.iter().cloned().collect(); - let futures = uris.iter().take(SHOW_EPISODE_LIMIT).map(|uri| { + let all_uris: Vec = show.episodes.iter().cloned().collect(); + let total = all_uris.len(); + let uris: Vec = all_uris.into_iter().skip(offset).take(limit).collect(); + let futures = uris.iter().map(|uri| { let session = session.clone(); let token = token.to_string(); let uri = uri.clone(); @@ -1467,6 +1507,8 @@ pub async fn collection( .collect(), artist_id: None, art_url: show_json.art_url.clone(), + total_items: total as i32, + has_more: offset + show_json.episodes.len() < total, }; Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) } @@ -1480,21 +1522,27 @@ pub async fn artist(session: &Session, token: &str, id: &str) -> Result = artist .albums_current() - .take(ARTIST_ALBUM_LIMIT) - .chain(artist.singles_current().take(ARTIST_ALBUM_LIMIT)) + .chain(artist.singles_current()) .map(|uri| uri_of(&id_of(uri), "album")) .collect(); Ok(serde_json::to_string(&artist_json(&artist, album_ids)).map_err(|e| e.to_string())?) } -pub async fn albums_by(session: &Session, token: &str, id: &str) -> Result { +pub async fn albums_by( + session: &Session, + token: &str, + id: &str, + offset: usize, + limit: usize, +) -> Result { let artist = artist_get(session, token, &spotify_uri(id, "artist")?) .await .map_err(|e| e.to_string())?; let uris: Vec = artist .albums_current() - .take(ARTIST_ALBUM_LIMIT) - .chain(artist.singles_current().take(ARTIST_ALBUM_LIMIT)) + .chain(artist.singles_current()) + .skip(offset) + .take(limit.clamp(1, MAX_PAGE)) .cloned() .collect(); let mut albums = fetch_albums(session, token, &uris, false).await; @@ -1502,7 +1550,13 @@ pub async fn albums_by(session: &Session, token: &str, id: &str) -> Result Result { +pub async fn top_tracks( + session: &Session, + token: &str, + id: &str, + offset: usize, + limit: usize, +) -> Result { let artist = artist_get(session, token, &spotify_uri(id, "artist")?) .await .map_err(|e| e.to_string())?; @@ -1511,9 +1565,10 @@ pub async fn top_tracks(session: &Session, token: &str, id: &str) -> Result jni::sys::jstring { let Some(filter) = string(&mut env, &filter) else { return std::ptr::null_mut(); }; let result = engine::with_session_and_token(|session, token| { - engine::run(catalog::library(session, token, &filter)) + engine::run(catalog::library( + session, + token, + &filter, + offset.max(0) as usize, + limit.max(1) as usize, + )) }); catalog_result(&mut env, result) } @@ -206,12 +214,21 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB _this: JObject, kind: JString, id: JString, + offset: jlong, + limit: jlong, ) -> jni::sys::jstring { let (Some(kind), Some(id)) = (string(&mut env, &kind), string(&mut env, &id)) else { return std::ptr::null_mut(); }; let result = engine::with_session_and_token(|session, token| { - engine::run(catalog::collection(session, token, &kind, &id)) + engine::run(catalog::collection( + session, + token, + &kind, + &id, + offset.max(0) as usize, + limit.max(1) as usize, + )) }); catalog_result(&mut env, result) } @@ -236,12 +253,20 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB mut env: JNIEnv, _this: JObject, id: JString, + offset: jlong, + limit: jlong, ) -> jni::sys::jstring { let Some(id) = string(&mut env, &id) else { return std::ptr::null_mut(); }; let result = engine::with_session_and_token(|session, token| { - engine::run(catalog::albums_by(session, token, &id)) + engine::run(catalog::albums_by( + session, + token, + &id, + offset.max(0) as usize, + limit.max(1) as usize, + )) }); catalog_result(&mut env, result) } @@ -251,12 +276,20 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB mut env: JNIEnv, _this: JObject, id: JString, + offset: jlong, + limit: jlong, ) -> jni::sys::jstring { let Some(id) = string(&mut env, &id) else { return std::ptr::null_mut(); }; let result = engine::with_session_and_token(|session, token| { - engine::run(catalog::top_tracks(session, token, &id)) + engine::run(catalog::top_tracks( + session, + token, + &id, + offset.max(0) as usize, + limit.max(1) as usize, + )) }); catalog_result(&mut env, result) } @@ -267,6 +300,7 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB _this: JObject, query: JString, scopes: JString, + offset: jlong, limit: jlong, ) -> jni::sys::jstring { let (Some(query), Some(scopes)) = (string(&mut env, &query), string(&mut env, &scopes)) else { @@ -278,6 +312,7 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB session, token, &query, + offset.max(0) as i32, limit as i32, &scopes, )) @@ -285,7 +320,6 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB catalog_result(&mut env, result) } - #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeCollectionContains( mut env: JNIEnv, From 1bcb308a0f5a2d08c57380f07a830fa9fb1bef37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 14:12:34 +0100 Subject: [PATCH 30/46] fix(sidetracks): load shows reliably and page search sections Retry show and episode metadata once when Spotify's extended-metadata endpoint returns a transient error, and fall back to the context resolver for show details and episode order. Mark video episodes as non-playable. Show ten search results per section and let More reveal the next ten locally before fetching another page. The More row no longer reports how many items it will reveal. --- .../apps/spotify/SpotifySearchScreen.kt | 67 +++++---- native/sidekit-spotify/src/catalog.rs | 130 ++++++++++++++---- 2 files changed, 149 insertions(+), 48 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 1b6c19ed..6701f1ba 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -29,6 +29,8 @@ import hrln.interactive.sidekit.design.components.rememberSwipeRevealGroup import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme +private const val SEARCH_SECTION_PAGE = 10 + @Composable fun SpotifySearchScreen( state: SpotifyUiState, @@ -45,8 +47,14 @@ fun SpotifySearchScreen( onLoadMore: (SearchScope) -> Unit, onDismissNotice: () -> Unit ) { - var expandedTracks by remember(state.query) { mutableStateOf(false) } + var expanded by remember(state.query) { mutableStateOf(emptyMap()) } val results = state.searchResults + fun visible(scope: SearchScope, total: Int): Int = minOf(expanded[scope] ?: SEARCH_SECTION_PAGE, total) + fun showMore(scope: SearchScope, total: Int) { + val current = visible(scope, total) + expanded = expanded + (scope to (current + SEARCH_SECTION_PAGE)) + if (current >= total && scope in state.searchHasMore) onLoadMore(scope) + } val actions = withNotice( state.notice, onDismissNotice, @@ -111,7 +119,7 @@ fun SpotifySearchScreen( var slot = 0 if (results.tracks.isNotEmpty()) { item(key = "tracksLabel") { SectionLabel("Tracks") } - val shown = if (expandedTracks) results.tracks else results.tracks.take(5) + val shown = results.tracks.take(visible(SearchScope.Tracks, results.tracks.size)) itemsIndexed(shown, key = { _, track -> "t${track.id}" }) { index, track -> SidekitSwipeRow( key = track.id, @@ -129,50 +137,61 @@ fun SpotifySearchScreen( } } slot += shown.size - if (!expandedTracks && results.tracks.size > 5) { - item(key = "moreTracks") { TerminalRow("More tracks", secondary = "${results.tracks.size - 5} more", onClick = { expandedTracks = true }) } - } - if (expandedTracks && SearchScope.Tracks in state.searchHasMore) { - item(key = "moreTrackPages") { SearchMoreRow(SearchScope.Tracks, "More tracks", state, onLoadMore) } + if (shown.size < results.tracks.size || SearchScope.Tracks in state.searchHasMore) { + item(key = "moreTracks") { + SearchMoreRow(SearchScope.Tracks, "More tracks", state) { showMore(SearchScope.Tracks, results.tracks.size) } + } } } if (results.artists.isNotEmpty()) { item(key = "artistsLabel") { SectionLabel("Artists") } - itemsIndexed(results.artists, key = { _, artist -> "r${artist.id}" }) { index, artist -> + val shown = results.artists.take(visible(SearchScope.Artists, results.artists.size)) + itemsIndexed(shown, key = { _, artist -> "r${artist.id}" }) { index, artist -> SpotifyLibraryRow(artist.artRes, artist.name, "Artist", Modifier.listFade(listState, slot + index), artUrl = artist.artUrl) { onArtist(artist) } } - slot += results.artists.size - if (SearchScope.Artists in state.searchHasMore) { - item(key = "moreArtists") { SearchMoreRow(SearchScope.Artists, "More artists", state, onLoadMore) } + slot += shown.size + if (shown.size < results.artists.size || SearchScope.Artists in state.searchHasMore) { + item(key = "moreArtists") { + SearchMoreRow(SearchScope.Artists, "More artists", state) { showMore(SearchScope.Artists, results.artists.size) } + } } } if (results.albums.isNotEmpty()) { item(key = "albumsLabel") { SectionLabel("Albums") } - itemsIndexed(results.albums, key = { _, album -> "a${album.id}" }) { index, album -> + val shown = results.albums.take(visible(SearchScope.Albums, results.albums.size)) + itemsIndexed(shown, key = { _, album -> "a${album.id}" }) { index, album -> SpotifyLibraryRow(album.artRes, album.title, "${album.artist} · ${album.year}", Modifier.listFade(listState, slot + index), artUrl = album.artUrl) { onAlbum(album) } } - slot += results.albums.size - if (SearchScope.Albums in state.searchHasMore) { - item(key = "moreAlbums") { SearchMoreRow(SearchScope.Albums, "More albums", state, onLoadMore) } + slot += shown.size + if (shown.size < results.albums.size || SearchScope.Albums in state.searchHasMore) { + item(key = "moreAlbums") { + SearchMoreRow(SearchScope.Albums, "More albums", state) { showMore(SearchScope.Albums, results.albums.size) } + } } } if (results.playlists.isNotEmpty()) { item(key = "playlistsLabel") { SectionLabel("Playlists") } - itemsIndexed(results.playlists, key = { _, playlist -> "p${playlist.id}" }) { index, playlist -> + val shown = results.playlists.take(visible(SearchScope.Playlists, results.playlists.size)) + itemsIndexed(shown, key = { _, playlist -> "p${playlist.id}" }) { index, playlist -> SpotifyLibraryRow(playlist.artRes, playlist.name, "${playlist.owner} · ${playlist.trackCount} tracks", Modifier.listFade(listState, slot + index), artUrl = playlist.artUrl) { onPlaylist(playlist) } } - slot += results.playlists.size - if (SearchScope.Playlists in state.searchHasMore) { - item(key = "morePlaylists") { SearchMoreRow(SearchScope.Playlists, "More playlists", state, onLoadMore) } + slot += shown.size + if (shown.size < results.playlists.size || SearchScope.Playlists in state.searchHasMore) { + item(key = "morePlaylists") { + SearchMoreRow(SearchScope.Playlists, "More playlists", state) { showMore(SearchScope.Playlists, results.playlists.size) } + } } } if (results.shows.isNotEmpty()) { item(key = "showsLabel") { SectionLabel("Shows") } - itemsIndexed(results.shows, key = { _, show -> "s${show.id}" }) { index, show -> + val shown = results.shows.take(visible(SearchScope.Shows, results.shows.size)) + itemsIndexed(shown, key = { _, show -> "s${show.id}" }) { index, show -> SpotifyLibraryRow(show.artRes, show.name, show.publisher, Modifier.listFade(listState, slot + index), artUrl = show.artUrl) { onShow(show) } } - if (SearchScope.Shows in state.searchHasMore) { - item(key = "moreShows") { SearchMoreRow(SearchScope.Shows, "More shows", state, onLoadMore) } + if (shown.size < results.shows.size || SearchScope.Shows in state.searchHasMore) { + item(key = "moreShows") { + SearchMoreRow(SearchScope.Shows, "More shows", state) { showMore(SearchScope.Shows, results.shows.size) } + } } } } @@ -186,12 +205,12 @@ private fun SearchMoreRow( scope: SearchScope, label: String, state: SpotifyUiState, - onLoadMore: (SearchScope) -> Unit + onMore: () -> Unit ) { val loading = scope in state.searchMorePending TerminalRow( label, secondary = if (loading) "Loading…" else null, - onClick = if (loading) null else ({ onLoadMore(scope) }) + onClick = if (loading) null else onMore ) } diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs index aa17c7f8..26ac71ff 100644 --- a/native/sidekit-spotify/src/catalog.rs +++ b/native/sidekit-spotify/src/catalog.rs @@ -273,16 +273,21 @@ fn artist_json(artist: &Artist, album_ids: Vec) -> ArtistJson { } } -fn episode_json(episode: &Episode, show: &Show) -> EpisodeJson { +fn episode_json_with( + episode: &Episode, + show_name: &str, + show_id: &str, + show_art_url: Option, +) -> EpisodeJson { EpisodeJson { id: id_of(&episode.id), title: episode.name.clone(), show: if episode.show_name.is_empty() { - show.name.clone() + show_name.to_string() } else { episode.show_name.clone() }, - show_id: id_of(&show.id), + show_id: show_id.to_string(), published: { let date = episode.publish_time.as_utc(); format!( @@ -294,9 +299,9 @@ fn episode_json(episode: &Episode, show: &Show) -> EpisodeJson { }, art_res: 0, duration_ms: episode.duration as i64, - video: false, + video: !episode.videos.is_empty(), uri: uri_of(&id_of(&episode.id), "episode"), - art_url: image_url(&episode.covers).or_else(|| image_url(&show.covers)), + art_url: image_url(&episode.covers).or(show_art_url), } } @@ -362,6 +367,23 @@ async fn sp_request( http_request(session, token, method, &url, content_type, body).await } +async fn metadata_bytes_retry( + session: &Session, + token: &str, + kind: ExtensionKind, + uri: &SpotifyUri, +) -> Result { + match metadata_bytes(session, token, kind, uri).await { + Ok(bytes) => Ok(bytes), + Err(first) => { + // Spotify's extended-metadata endpoint occasionally answers the first request for a + // podcast show with a 502. A second request for the same entity succeeds. + log::warn!("metadata request failed, retrying: {first}"); + metadata_bytes(session, token, kind, uri).await + } + } +} + async fn metadata_bytes( session: &Session, token: &str, @@ -427,14 +449,14 @@ async fn artist_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result< } async fn show_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { - let bytes = metadata_bytes(session, token, ExtensionKind::SHOW_V4, uri).await?; + let bytes = metadata_bytes_retry(session, token, ExtensionKind::SHOW_V4, uri).await?; let message = librespot_protocol::metadata::Show::parse_from_bytes(&bytes).map_err(|e| e.to_string())?; Show::parse(&message, uri).map_err(|e| e.to_string()) } async fn episode_get(session: &Session, token: &str, uri: &SpotifyUri) -> Result { - let bytes = metadata_bytes(session, token, ExtensionKind::EPISODE_V4, uri).await?; + let bytes = metadata_bytes_retry(session, token, ExtensionKind::EPISODE_V4, uri).await?; let message = librespot_protocol::metadata::Episode::parse_from_bytes(&bytes) .map_err(|e| e.to_string())?; Episode::parse(&message, uri).map_err(|e| e.to_string()) @@ -473,6 +495,25 @@ async fn context_get(session: &Session, token: &str, uri: &str) -> Result(&text).map_err(|e| e.to_string()) } +fn context_episode_uris(context: &Context) -> Vec { + let mut seen = HashSet::new(); + let mut uris = Vec::new(); + for page in context.pages.iter() { + for track in page.tracks.iter() { + let Some(uri) = track.uri.as_deref() else { + continue; + }; + if !uri.starts_with("spotify:episode:") || !seen.insert(uri.to_string()) { + continue; + } + if let Ok(uri) = SpotifyUri::from_uri(uri) { + uris.push(uri); + } + } + } + uris +} + async fn fetch_tracks( session: &Session, token: &str, @@ -1469,46 +1510,87 @@ pub async fn collection( Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) } "Show" => { - let show = show_get(session, token, &spotify_uri(id, "show")?) - .await - .map_err(|e| e.to_string())?; - let all_uris: Vec = show.episodes.iter().cloned().collect(); + let show_uri = spotify_uri(id, "show")?; + let show = show_get(session, token, &show_uri).await; + let (title, publisher, art_url, all_uris) = match show { + Ok(show) => { + let mut uris: Vec = show.episodes.iter().cloned().collect(); + if uris.is_empty() { + // Some shows return no episode list in their metadata. The context + // resolver still knows the full episode order. + uris = context_get(session, token, &uri_of(id, "show")) + .await + .map(|context| context_episode_uris(&context)) + .unwrap_or_default(); + } + ( + show.name.clone(), + if show.publisher.is_empty() { + "Podcast".to_string() + } else { + show.publisher.clone() + }, + image_url(&show.covers), + uris, + ) + } + Err(error) => { + log::warn!("show metadata failed, using context: {error}"); + let context = context_get(session, token, &uri_of(id, "show")).await?; + let uris = context_episode_uris(&context); + let title = context + .metadata + .get("title") + .cloned() + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| "Show".to_string()); + let publisher = context + .metadata + .get("publisher") + .cloned() + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| "Podcast".to_string()); + let art_url = context + .metadata + .get("image_url") + .cloned() + .filter(|value| !value.is_empty()); + (title, publisher, art_url, uris) + } + }; let total = all_uris.len(); let uris: Vec = all_uris.into_iter().skip(offset).take(limit).collect(); let futures = uris.iter().map(|uri| { let session = session.clone(); let token = token.to_string(); let uri = uri.clone(); - let show = show.clone(); + let title = title.clone(); + let show_id = id.to_string(); + let art_url = art_url.clone(); async move { episode_get(&session, &token, &uri) .await .ok() - .map(|episode| episode_json(&episode, &show)) + .map(|episode| episode_json_with(&episode, &title, &show_id, art_url)) } }); let episodes: Vec = join_all(futures).await.into_iter().flatten().collect(); - let show_json = show_json(&show, episodes); - let subtitle = format!( - "{} · {} episodes", - show_json.publisher, show_json.episode_count - ); + let subtitle = format!("{publisher} · {total} episodes"); let collection = CollectionJson { kind: "Show".to_string(), - id: show_json.id.clone(), - title: show_json.name.clone(), + id: id.to_string(), + title, subtitle, art_res: 0, - items: show_json - .episodes + items: episodes .iter() .map(|episode| serde_json::to_value(episode).unwrap_or(Value::Null)) .collect(), artist_id: None, - art_url: show_json.art_url.clone(), + art_url, total_items: total as i32, - has_more: offset + show_json.episodes.len() < total, + has_more: offset + episodes.len() < total, }; Ok(serde_json::to_string(&collection).map_err(|e| e.to_string())?) } From 70c32d60b916b633766106f6953337daacd7ad32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 14:22:10 +0100 Subject: [PATCH 31/46] refactor(sideplay): rename Sidetracks to Sideplay --- app/build.gradle.kts | 4 ++-- ...idetracks_album.png => sideplay_album.png} | Bin ...etracks_artist.png => sideplay_artist.png} | Bin app/screenshots/sideplay_home.png | Bin 0 -> 51202 bytes app/screenshots/sideplay_library_albums.png | Bin 0 -> 52131 bytes ...w_playing.png => sideplay_now_playing.png} | Bin ...idetracks_queue.png => sideplay_queue.png} | Bin ...etracks_search.png => sideplay_search.png} | Bin ...cks_settings.png => sideplay_settings.png} | Bin app/screenshots/sideplay_sign_in.png | Bin 0 -> 33435 bytes app/screenshots/sidetracks_home.png | Bin 51684 -> 0 bytes app/screenshots/sidetracks_library_albums.png | Bin 52732 -> 0 bytes app/screenshots/sidetracks_sign_in.png | Bin 33859 -> 0 bytes .../SpotifyPlaybackInstrumentedTest.kt | 2 +- .../SpotifyPlaybackServiceInstrumentedTest.kt | 4 ++-- app/src/main/AndroidManifest.xml | 2 +- .../hrln/interactive/sidekit/MainActivity.kt | 6 ++--- .../sidekit/apps/spotify/SpotifyGraph.kt | 16 ++++++------- .../apps/spotify/SpotifyLibraryScreens.kt | 6 ++--- .../apps/spotify/SpotifySettingsScreens.kt | 4 ++-- .../sidekit/apps/spotify/SpotifyViewModel.kt | 6 ++--- .../auth/EncryptedSpotifyCredentialStore.kt | 1 + .../sidekit/apps/spotify/auth/SpotifyAuth.kt | 4 ++-- .../apps/spotify/data/NativeSpotifyCatalog.kt | 2 +- .../apps/spotify/data/SpotifySession.kt | 4 +++- .../apps/spotify/data/SpotifySettings.kt | 3 ++- .../apps/spotify/data/SpotifyStateStores.kt | 4 ++-- .../apps/spotify/data/SpotifyWebApi.kt | 4 ++-- .../apps/spotify/playback/SpotifyPlayback.kt | 8 +++---- .../playback/SpotifyPlaybackService.kt | 16 ++++++------- .../sidekit/navigation/SidekitNavHost.kt | 10 ++++---- .../interactive/sidekit/navigation/SubApp.kt | 2 +- ...racksLinksTest.kt => SideplayLinksTest.kt} | 8 +++---- ...nshotTest.kt => SideplayScreenshotTest.kt} | 22 +++++++++--------- .../apps/spotify/SpotifyPersistenceTest.kt | 2 +- .../apps/spotify/SpotifyPlaybackTest.kt | 2 +- docs/spotify/{SIDETRACKS.md => SIDEPLAY.md} | 19 ++++++++------- native/sidekit-spotify/README.md | 4 ++-- native/sidekit-spotify/src/catalog.rs | 2 +- native/sidekit-spotify/src/lib.rs | 2 +- 40 files changed, 87 insertions(+), 82 deletions(-) rename app/screenshots/{sidetracks_album.png => sideplay_album.png} (100%) rename app/screenshots/{sidetracks_artist.png => sideplay_artist.png} (100%) create mode 100644 app/screenshots/sideplay_home.png create mode 100644 app/screenshots/sideplay_library_albums.png rename app/screenshots/{sidetracks_now_playing.png => sideplay_now_playing.png} (100%) rename app/screenshots/{sidetracks_queue.png => sideplay_queue.png} (100%) rename app/screenshots/{sidetracks_search.png => sideplay_search.png} (100%) rename app/screenshots/{sidetracks_settings.png => sideplay_settings.png} (100%) create mode 100644 app/screenshots/sideplay_sign_in.png delete mode 100644 app/screenshots/sidetracks_home.png delete mode 100644 app/screenshots/sidetracks_library_albums.png delete mode 100644 app/screenshots/sidetracks_sign_in.png rename app/src/test/java/hrln/interactive/sidekit/apps/spotify/{SidetracksLinksTest.kt => SideplayLinksTest.kt} (63%) rename app/src/test/java/hrln/interactive/sidekit/apps/spotify/{SidetracksScreenshotTest.kt => SideplayScreenshotTest.kt} (89%) rename docs/spotify/{SIDETRACKS.md => SIDEPLAY.md} (94%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 447ec236..67190c32 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -109,7 +109,7 @@ android { } } -// Sidetracks' librespot bridge. The generated ABI directory is a normal jniLibs source, so an +// Sideplay' librespot bridge. The generated ABI directory is a normal jniLibs source, so an // unsupported ABI simply has no .so and the Kotlin bridge reports it as unavailable. val spotifyJniLibs = layout.buildDirectory.dir("generated/spotifyJniLibs") val cargo = listOf( @@ -123,7 +123,7 @@ val buildSpotifyNative = tasks.register("buildSpotifyNative") { group = "native" enabled = cargo != null if (cargo == null) { - logger.warn("Sidetracks: cargo is unavailable; libsidekit_spotify.so will not be packaged.") + logger.warn("Sideplay: cargo is unavailable; libsidekit_spotify.so will not be packaged.") } workingDir = rootProject.file("native/sidekit-spotify") commandLine( diff --git a/app/screenshots/sidetracks_album.png b/app/screenshots/sideplay_album.png similarity index 100% rename from app/screenshots/sidetracks_album.png rename to app/screenshots/sideplay_album.png diff --git a/app/screenshots/sidetracks_artist.png b/app/screenshots/sideplay_artist.png similarity index 100% rename from app/screenshots/sidetracks_artist.png rename to app/screenshots/sideplay_artist.png diff --git a/app/screenshots/sideplay_home.png b/app/screenshots/sideplay_home.png new file mode 100644 index 0000000000000000000000000000000000000000..737f9f8366331ffa980fcd080dbc5b53f3d8770f GIT binary patch literal 51202 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ℜ>vX<87LZQ-{W-fL@4(LlhZ#s=4n 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 z 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 z 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 RIt1WPPD)v_T*Aoz{{Ys5DPRBq diff --git a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt index 88fdf276..8443b4b3 100644 --- a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt +++ b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackInstrumentedTest.kt @@ -61,7 +61,7 @@ class SpotifyPlaybackInstrumentedTest { } @Test - fun activatingAnotherMediaOwnerPausesSidetracks() { + fun activatingAnotherMediaOwnerPausesSideplay() { val gateway = FakeGateway() val registry = MediaSessionRegistry() val playback = SpotifyPlayback(gateway, scope, registry, context = silentContext) diff --git a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt index cbf29242..c8848ac8 100644 --- a/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt +++ b/app/src/androidTest/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackServiceInstrumentedTest.kt @@ -62,13 +62,13 @@ class SpotifyPlaybackServiceInstrumentedTest { } val notification = waitForNotification() - assertEquals("sidetracks", notification.channelId) + assertEquals("sideplay", notification.channelId) assertEquals("Service track", notification.extras.getCharSequence(Notification.EXTRA_TITLE)?.toString()) assertEquals("Artist", notification.extras.getCharSequence(Notification.EXTRA_TEXT)?.toString()) assertEquals(listOf("Previous", "Pause", "Next"), notification.actions.map { it.title.toString() }) assertNotNull(notification.contentIntent) assertTrue(notification.flags and Notification.FLAG_ONGOING_EVENT != 0) - assertTrue(manager.getNotificationChannel("sidetracks") != null) + assertTrue(manager.getNotificationChannel("sideplay") != null) } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ac734375..5fcf9b45 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -228,7 +228,7 @@ android:name=".apps.music.MusicService" android:exported="false" android:foregroundServiceType="mediaPlayback" /> - + val vm = entry.spotifyViewModel(nav) @@ -84,7 +84,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { LaunchedEffect(state.signedIn) { if (!state.signedIn) nav.navigate(SIGN_IN) { launchSingleTop = true } } - SidetracksHomeScreen( + SideplayHomeScreen( state, onNowPlaying = { nav.navigate(NOW_PLAYING) }, onLibrary = { nav.navigate(LIBRARY) }, @@ -290,7 +290,7 @@ fun NavGraphBuilder.sidetracksGraph(nav: NavHostController) { @Composable private fun MissingCollectionScreen(onBack: () -> Unit, loading: Boolean = false) { - SidekitScaffold(title = "Sidetracks", actions = spotifyBack("Back", onBack)) { padding -> + SidekitScaffold(title = "Sideplay", actions = spotifyBack("Back", onBack)) { padding -> androidx.compose.foundation.layout.Box( androidx.compose.ui.Modifier.padding(padding) ) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 2b8055cc..3aa1ee5b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -30,7 +30,7 @@ import hrln.interactive.sidekit.design.icons.SidekitIcon import hrln.interactive.sidekit.design.theme.SidekitTheme @Composable -fun SidetracksHomeScreen( +fun SideplayHomeScreen( state: SpotifyUiState, onNowPlaying: () -> Unit, onLibrary: () -> Unit, @@ -52,7 +52,7 @@ fun SidetracksHomeScreen( ) ) SidekitScaffold( - title = "Sidetracks", + title = "Sideplay", actions = bar, options = listOf( SideAction("refresh", "Refresh", SidekitIcon.Undo, onClick = onRefresh), @@ -119,7 +119,7 @@ fun LibraryScreen( val actions = withNotice( state.notice, onDismissNotice, - listOf(spotifyBack("Sidetracks", onBack).first(), SideAction("search", "Search", SidekitIcon.Search, motion = ActionMotion.Forward, onClick = onSearch)) + listOf(spotifyBack("Sideplay", onBack).first(), SideAction("search", "Search", SidekitIcon.Search, motion = ActionMotion.Forward, onClick = onSearch)) ) val filters = LibraryFilter.entries.map { filter -> SideAction( diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index 6573bd33..92c075fe 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -76,7 +76,7 @@ fun SpotifySettingsScreen( title = "Settings", actions = actions, options = listOf( - SideAction("about", "About", SidekitIcon.Docs, onClick = { onNotice("Sidetracks · a Sidekit sub-app") }), + SideAction("about", "About", SidekitIcon.Docs, onClick = { onNotice("Sideplay · a Sidekit sub-app") }), SideAction("cache", "Clear cache", SidekitIcon.DeleteForever, onClick = onClearCache) ) ) { padding -> @@ -164,7 +164,7 @@ fun SpotifySignInScreen( item(key = "explain") { SidekitCard(Modifier.listFade(listState, 0).fillMaxWidth()) { Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - SidekitText("Sidetracks plays from your Spotify account.", style = SidekitTheme.typography.bodySmall, maxLines = 2) + SidekitText("Sideplay plays from your Spotify account.", style = SidekitTheme.typography.bodySmall, maxLines = 2) SidekitText( "Your password is entered in the browser, never here.", style = SidekitTheme.typography.caption, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 7c1ae7a9..b5cf5450 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -82,7 +82,7 @@ data class SpotifyUiState( } /** - * State holder for the Sidetracks UI. Catalog reads, queue ordering and local history live in + * State holder for the Sideplay UI. Catalog reads, queue ordering and local history live in * their own domain objects; this class only maps them to the single immutable screen state and * implements the optimistic save/follow behaviour the action bars expect. */ @@ -146,7 +146,7 @@ class SpotifyViewModel( } } - /** Adopts the process-wide player and queue after the Sidetracks screen was recreated. */ + /** Adopts the process-wide player and queue after the Sideplay screen was recreated. */ private fun restorePlaybackState() { val session = session ?: return val playback = session.playback @@ -1054,7 +1054,7 @@ class SpotifyViewModel( "stopped" -> if (matchesCurrent(event)) { // Stop from the notification or lock screen: clear both the UI and the queue so - // reopening Sidetracks cannot restore the track that was stopped. + // reopening Sideplay cannot restore the track that was stopped. session?.playback?.updateState(false, 0L) applyQueue(queue.stop(), positionMs = 0L, playing = false) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt index 20d85a1c..8073e6f3 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/EncryptedSpotifyCredentialStore.kt @@ -10,6 +10,7 @@ import org.json.JSONObject */ class EncryptedSpotifyCredentialStore( context: Context, + // Legacy name kept so existing dev installs keep their Spotify session. name: String = "sidetracks_spotify" ) : SpotifyCredentialStore { private val settings = PrivateSettings(context.applicationContext, name) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt index f54fe26d..b286612e 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt @@ -109,7 +109,7 @@ class SpotifyAuth( val target = requestLine.split(' ').getOrNull(1) ?: return SpotifyPkce.Callback.Ignored val result = SpotifyPkce.parseCallback(target, expectedState) val message = when (result) { - is SpotifyPkce.Callback.Code -> "Signed in to Sidetracks. You can close this page." + is SpotifyPkce.Callback.Code -> "Signed in to Sideplay. You can close this page." is SpotifyPkce.Callback.Failure -> "Sign-in did not complete. You can close this page." SpotifyPkce.Callback.Ignored -> "Waiting for Spotify…" } @@ -121,7 +121,7 @@ class SpotifyAuth( val body = """ - Sidetracks + Sideplay

$message

""".trimIndent() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt index 37133ba6..9fbe8f39 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt @@ -151,7 +151,7 @@ class NativeSpotifyCatalog( if (text.startsWith("{")) { val error = runCatching { JSONObject(text).optString("error") }.getOrDefault("") if (error.isNotEmpty()) { - android.util.Log.w("SidetracksCatalog", error) + android.util.Log.w("SideplayCatalog", error) val failure = if (error.contains("SESSION", ignoreCase = true)) { SpotifyFailure.AuthenticationExpired } else { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 9ac73cf7..cd18e8f5 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -34,6 +34,8 @@ class SpotifySession( private val appContext = context?.applicationContext private val settingsStore = appContext?.let { SpotifySettingsStore(it) } + + // Legacy file names kept so existing dev installs keep their queue and history. private val queueStore = appContext?.let { FileSpotifyQueueStore(File(it.filesDir, "sidetracks-queue.bin")) } private val historyStore = appContext?.let { FileSpotifyHistoryStore(File(it.filesDir, "sidetracks-history.bin")) } private val _settings = MutableStateFlow(settingsStore?.read() ?: SpotifySettings()) @@ -52,7 +54,7 @@ class SpotifySession( } val writer: SpotifyLibraryWriter? get() = catalog as? SpotifyLibraryWriter - /** Process-wide queue and history so leaving Sidetracks does not forget what is playing. */ + /** Process-wide queue and history so leaving Sideplay does not forget what is playing. */ val queue = SpotifyQueueRepository( load = { queueStore?.read() ?: SpotifyQueueRepository.Snapshot() }, save = { queueStore?.write(it) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt index 918aa4d2..0cbc9bb7 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt @@ -13,10 +13,11 @@ data class SpotifySettings( /** * Settings live in the same encrypted, atomic store as credentials. They are not secrets, but the - * store already exists, needs no extra dependency and keeps all Sidetracks state in one place. + * store already exists, needs no extra dependency and keeps all Sideplay state in one place. */ class SpotifySettingsStore( context: Context, + // Legacy name kept so existing dev installs keep their audio-quality setting. name: String = "sidetracks_settings" ) { private val settings = PrivateSettings(context.applicationContext, name) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt index fc99722a..697b7a2e 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyStateStores.kt @@ -217,12 +217,12 @@ private fun DataInputStream.readItem(): SpotifyMediaItem = when (val type = read artUrl = readNullableString() ) - else -> throw IOException("Unknown Sidetracks media item type $type") + else -> throw IOException("Unknown Sideplay media item type $type") } private fun DataInputStream.readCount(max: Int): Int { val count = readInt() - if (count !in 0..max) throw IOException("Invalid Sidetracks item count $count") + if (count !in 0..max) throw IOException("Invalid Sideplay item count $count") return count } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt index ca984639..88e9eda0 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt @@ -403,12 +403,12 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { status == 401 -> fail(SpotifyFailure.AuthenticationExpired, "Sign in again") status == 429 -> { - android.util.Log.w("SidetracksApi", "$url -> 429 retryAfter=${connection.getHeaderField("Retry-After")} ${text.take(300)}") + android.util.Log.w("SideplayApi", "$url -> 429 retryAfter=${connection.getHeaderField("Retry-After")} ${text.take(300)}") fail(SpotifyFailure.RateLimited, "Spotify is rate-limiting requests") } status !in 200..299 -> { - android.util.Log.w("SidetracksApi", "$url -> $status ${text.take(300)}") + android.util.Log.w("SideplayApi", "$url -> $status ${text.take(300)}") fail(SpotifyFailure.Network, "Spotify returned HTTP $status") } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index 3b48a543..d9d439eb 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -35,9 +35,9 @@ class SpotifyPlayback( private val audio: SpotifyAudioSink? = null, private val context: Context? = null ) : MediaSessionOwner { - override val sessionId = "sidetracks" - override val notificationChannelId = "sidetracks" - override val notificationChannelName = "Sidetracks playback" + override val sessionId = "sideplay" + override val notificationChannelId = "sideplay" + override val notificationChannelName = "Sideplay playback" override val notificationId = 0x6D76 @Volatile @@ -280,7 +280,7 @@ class SpotifyPlayback( private fun session(): MediaSession? { val context = context ?: return null - return mediaSession ?: MediaSession(context, "SideKit Sidetracks").also { session -> + return mediaSession ?: MediaSession(context, "SideKit Sideplay").also { session -> session.setCallback( object : MediaSession.Callback() { override fun onPlay() { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt index de9f7175..e240072b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt @@ -11,12 +11,12 @@ import android.content.pm.ServiceInfo import android.graphics.drawable.Icon import android.os.IBinder import hrln.interactive.sidekit.MainActivity -import hrln.interactive.sidekit.apps.spotify.SidetracksLinks +import hrln.interactive.sidekit.apps.spotify.SideplayLinks import hrln.interactive.sidekit.apps.spotify.data.SpotifySessions import hrln.interactive.sidekit.design.icons.SidekitIcon /** - * Keeps Sidetracks playing when SideKit is not in front. It mirrors [SpotifyPlayback]'s media + * Keeps Sideplay playing when SideKit is not in front. It mirrors [SpotifyPlayback]'s media * session in a platform media-style notification and forwards transport actions back to the * process-wide player. */ @@ -88,13 +88,13 @@ class SpotifyPlaybackService : Service() { this, 0, Intent(this, MainActivity::class.java) - .setAction(SidetracksLinks.ACTION_OPEN_NOW_PLAYING) + .setAction(SideplayLinks.ACTION_OPEN_NOW_PLAYING) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_IMMUTABLE ) return Notification.Builder(this, playback.notificationChannelId) .setSmallIcon(SidekitIcon.MusicNote2.drawable) - .setContentTitle(playback.current?.title ?: "Sidetracks") + .setContentTitle(playback.current?.title ?: "Sideplay") .setContentText(playback.current?.creator.orEmpty()) .setLargeIcon(playback.notificationArt) .setContentIntent(open) @@ -142,10 +142,10 @@ class SpotifyPlaybackService : Service() { ) companion object { - private const val ACTION_PLAY_PAUSE = "hrln.interactive.sidekit.sidetracks.PLAY_PAUSE" - private const val ACTION_NEXT = "hrln.interactive.sidekit.sidetracks.NEXT" - private const val ACTION_PREVIOUS = "hrln.interactive.sidekit.sidetracks.PREVIOUS" - private const val ACTION_STOP = "hrln.interactive.sidekit.sidetracks.STOP" + private const val ACTION_PLAY_PAUSE = "hrln.interactive.sidekit.sideplay.PLAY_PAUSE" + private const val ACTION_NEXT = "hrln.interactive.sidekit.sideplay.NEXT" + private const val ACTION_PREVIOUS = "hrln.interactive.sidekit.sideplay.PREVIOUS" + private const val ACTION_STOP = "hrln.interactive.sidekit.sideplay.STOP" private var running: SpotifyPlaybackService? = null diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt index cab07108..3ec53e5a 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SidekitNavHost.kt @@ -32,8 +32,8 @@ import hrln.interactive.sidekit.apps.sideport.openPorted import hrln.interactive.sidekit.apps.sideport.sideportGraph import hrln.interactive.sidekit.apps.sideticks.sideticksGraph import hrln.interactive.sidekit.apps.sideweather.sideweatherGraph -import hrln.interactive.sidekit.apps.spotify.SidetracksLinks -import hrln.interactive.sidekit.apps.spotify.sidetracksGraph +import hrln.interactive.sidekit.apps.spotify.SideplayLinks +import hrln.interactive.sidekit.apps.spotify.sideplayGraph import hrln.interactive.sidekit.apps.translate.translateGraph import hrln.interactive.sidekit.apps.tripplanning.tripPlanningGraph import hrln.interactive.sidekit.apps.vault.vaultGraph @@ -57,8 +57,8 @@ fun SidekitNavHost(navController: NavHostController = rememberNavController(), h IncomingLinks.Follow(navController) // A projected app's "Messages and media" opens its Sideport hub. SideportLinks.Follow(navController) - // Tapping the Sidetracks playback notification opens Now Playing. - SidetracksLinks.Follow(navController) + // Tapping the Sideplay playback notification opens Now Playing. + SideplayLinks.Follow(navController) // No screen transition. Every screen is the same frame (title, body, action bar) and the // title and bar are shared chrome that morph on their own, so navigating swaps only the body, // which plays its own entrance (rows rising in). Nothing moves as a whole screen, and only one @@ -88,7 +88,7 @@ fun SidekitNavHost(navController: NavHostController = rememberNavController(), h translateGraph(navController) calculatorGraph(navController) musicGraph(navController) - sidetracksGraph(navController) + sideplayGraph(navController) cameraGraph(navController) sideweatherGraph(navController) // Sidephone everyday apps (96:419) diff --git a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt index 3a19b97a..f1d0cb4c 100644 --- a/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt +++ b/app/src/main/java/hrln/interactive/sidekit/navigation/SubApp.kt @@ -21,7 +21,7 @@ enum class SubApp(val label: String, val icon: SidekitIcon, val designed: Boolea Files("Files", SidekitIcon.FolderCopy), Maps("Maps", SidekitIcon.Map), Music("Music", SidekitIcon.MusicNote2), - Sidetracks("Sidetracks", SidekitIcon.QueueMusic), + Sideplay("Sideplay", SidekitIcon.QueueMusic), Clock("Clock", SidekitIcon.Alarm), Video("Video", SidekitIcon.SmartDisplay), Vault("Vault", SidekitIcon.Encrypted), diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt similarity index 63% rename from app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt rename to app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt index e2744e64..77472306 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksLinksTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt @@ -8,13 +8,13 @@ import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) -class SidetracksLinksTest { +class SideplayLinksTest { @Test fun `the playback notification action asks for now playing`() { - assertTrue(SidetracksLinks.handle(Intent(SidetracksLinks.ACTION_OPEN_NOW_PLAYING))) + assertTrue(SideplayLinks.handle(Intent(SideplayLinks.ACTION_OPEN_NOW_PLAYING))) } @Test fun `unrelated intents are ignored`() { - assertFalse(SidetracksLinks.handle(Intent(Intent.ACTION_VIEW))) - assertFalse(SidetracksLinks.handle(null)) + assertFalse(SideplayLinks.handle(Intent(Intent.ACTION_VIEW))) + assertFalse(SideplayLinks.handle(null)) } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt similarity index 89% rename from app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt rename to app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt index 730a49ea..9af9146f 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SidetracksScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt @@ -14,7 +14,7 @@ import org.robolectric.annotation.GraphicsMode @RunWith(AndroidJUnit4::class) @GraphicsMode(GraphicsMode.Mode.NATIVE) -class SidetracksScreenshotTest { +class SideplayScreenshotTest { @get:Rule val compose = createComposeRule() private fun shoot(name: String, content: @Composable () -> Unit) { @@ -37,8 +37,8 @@ class SidetracksScreenshotTest { filter = LibraryFilter.Albums ) - @Test fun home() = shoot("sidetracks_home") { - SidetracksHomeScreen( + @Test fun home() = shoot("sideplay_home") { + SideplayHomeScreen( state, onNowPlaying = {}, onLibrary = {}, @@ -52,7 +52,7 @@ class SidetracksScreenshotTest { ) } - @Test fun library() = shoot("sidetracks_library_albums") { + @Test fun library() = shoot("sideplay_library_albums") { LibraryScreen( state, onBack = {}, @@ -69,7 +69,7 @@ class SidetracksScreenshotTest { ) } - @Test fun album() = shoot("sidetracks_album") { + @Test fun album() = shoot("sideplay_album") { CollectionScreen( runBlocking { MockSpotifyDataSource.collection(SpotifyCollectionType.Album, "night_signals") }!!, state, @@ -88,7 +88,7 @@ class SidetracksScreenshotTest { ) } - @Test fun artist() = shoot("sidetracks_artist") { + @Test fun artist() = shoot("sideplay_artist") { val artist = MockSpotifyDataSource.artists.first() ArtistScreen( artist, @@ -112,7 +112,7 @@ class SidetracksScreenshotTest { ) } - @Test fun search() = shoot("sidetracks_search") { + @Test fun search() = shoot("sideplay_search") { SpotifySearchScreen( state.copy(query = "night"), onBack = {}, @@ -130,7 +130,7 @@ class SidetracksScreenshotTest { ) } - @Test fun nowPlaying() = shoot("sidetracks_now_playing") { + @Test fun nowPlaying() = shoot("sideplay_now_playing") { SpotifyNowPlayingScreen( state, onBack = {}, @@ -149,7 +149,7 @@ class SidetracksScreenshotTest { ) } - @Test fun queue() = shoot("sidetracks_queue") { + @Test fun queue() = shoot("sideplay_queue") { QueueScreen( state, onBack = {}, @@ -165,7 +165,7 @@ class SidetracksScreenshotTest { ) } - @Test fun settings() = shoot("sidetracks_settings") { + @Test fun settings() = shoot("sideplay_settings") { SpotifySettingsScreen( state, onBack = {}, @@ -178,7 +178,7 @@ class SidetracksScreenshotTest { ) } - @Test fun signIn() = shoot("sidetracks_sign_in") { + @Test fun signIn() = shoot("sideplay_sign_in") { SpotifySignInScreen( SpotifyUiState(signedIn = false), onBack = {}, diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt index d08d68c8..82d6449a 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPersistenceTest.kt @@ -73,7 +73,7 @@ class SpotifyPersistenceTest { assertEquals(SpotifyQueueRepository.Snapshot(), snapshot) } - private fun tempFile(name: String) = File(context.cacheDir, "sidetracks-$name-${System.nanoTime()}.bin").also { it.delete() } + private fun tempFile(name: String) = File(context.cacheDir, "sideplay-$name-${System.nanoTime()}.bin").also { it.delete() } private fun track(id: String) = SpotifyTrack( id = id, diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt index 3f8d37d9..cf520389 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyPlaybackTest.kt @@ -34,7 +34,7 @@ class SpotifyPlaybackTest { @OptIn(ExperimentalCoroutinesApi::class) @Test - fun `starting another owner pauses Sidetracks`() = runTest { + fun `starting another owner pauses Sideplay`() = runTest { val gateway = FakeGateway() val registry = MediaSessionRegistry() val playback = SpotifyPlayback(gateway, CoroutineScope(StandardTestDispatcher(testScheduler)), registry) diff --git a/docs/spotify/SIDETRACKS.md b/docs/spotify/SIDEPLAY.md similarity index 94% rename from docs/spotify/SIDETRACKS.md rename to docs/spotify/SIDEPLAY.md index 3a440330..31cd5fd0 100644 --- a/docs/spotify/SIDETRACKS.md +++ b/docs/spotify/SIDEPLAY.md @@ -1,10 +1,10 @@ -# Sidetracks +# Sideplay **Updated:** 2026-11-09 -**Branch:** `feat/sidetracks` -**Phase:** feature-complete for daily use; pre-merge hardening and parity work remain +**Branch:** `integration` +**Phase:** PR review; P0 device verification and P1 parity decisions remain -Sidetracks is Sidekit's Spotify sub-app: one account's library, search, playback, and queue, +Sideplay is Sidekit's Spotify sub-app: one account's library, search, playback, and queue, drawn with Sidekit's chrome and operated keypad-first on a 480 × 640 phone. It is not a Spotify client reproduction and not a web wrapper. @@ -68,15 +68,16 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac ViewModel adopts the live item, position, and playing state. Starting a new item releases the previous `AudioTrack`. - Queue, queue history, shuffle order, repeat mode, and recently played persist across process - death in atomic binary files (`sidetracks-queue.bin`, `sidetracks-history.bin`). Playback + death in atomic binary files (`sidetracks-queue.bin`, `sidetracks-history.bin`; legacy names + kept so existing dev installs keep their data). Playback itself does not resume on launch. - Foreground `mediaPlayback` service and platform `MediaSession` keep librespot alive with the screen off. Notification shows artwork, title, artist, and previous/play-pause/next. When the ViewModel is gone, next/previous fall back to advancing the process-wide queue directly. - Tapping the notification opens Sidetracks directly at Now Playing rather than the launcher. + Tapping the notification opens Sideplay directly at Now Playing rather than the launcher. A native `stopped` event clears Now Playing and the queue so notification/lock-screen stop cannot leave stale UI or restore the stopped track. -- Sidetracks and local Music pause each other through `MediaSessions`; Sidetracks takes and +- Sideplay and local Music pause each other through `MediaSessions`; Sideplay takes and abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. - OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, releases the media session, removes the notification, then clears credentials. @@ -157,7 +158,7 @@ cross-cutting refactor. --- -## 6. Native build, provenance, and licensing +## 5. Native build, provenance, and licensing **Native bridge.** `native/sidekit-spotify` links librespot 0.8.0 and exposes the JNI surface declared in `apps/spotify/bridge/NativeBridge.kt`. @@ -180,7 +181,7 @@ declared in `apps/spotify/bridge/NativeBridge.kt`. otherwise reports `INVALID_CREDENTIALS`/`BAD_REQUEST`. - Scope: one constant. No protocol, playback, audio, or auth logic changes. - Removal: delete the patch once librespot supports overriding platform/client identity, or once - Sidetracks performs its own Android-client OAuth. + Sideplay performs its own Android-client OAuth. - Verify with `diff -rq ~/.cargo/registry/src/index.crates.io-*/librespot-core-0.8.0 native/vendor/librespot-core`; only `src/config.rs` (plus registry bookkeeping) should differ. diff --git a/native/sidekit-spotify/README.md b/native/sidekit-spotify/README.md index 6a7c6eef..35dbfb17 100644 --- a/native/sidekit-spotify/README.md +++ b/native/sidekit-spotify/README.md @@ -1,6 +1,6 @@ # libsidekit_spotify -The Sidetracks native bridge. It links librespot 0.8.0 and exposes the JNI surface declared in +The Sideplay native bridge. It links librespot 0.8.0 and exposes the JNI surface declared in `apps/spotify/bridge/NativeBridge.kt`. ## Toolchain @@ -37,4 +37,4 @@ incompatible `vergen-lib`. Do not update that crate without rebuilding Android t One minimal librespot-core patch is applied through `native/vendor/librespot-core` (platform constant set to `"linux"` so Login5/client-token requests match the desktop client id). It is -documented in `docs/spotify/SIDETRACKS.md` §6. Upstream MIT licence: `LICENSE-librespot`. +documented in `docs/spotify/SIDEPLAY.md` §6. Upstream MIT licence: `LICENSE-librespot`. diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs index 26ac71ff..3ca93037 100644 --- a/native/sidekit-spotify/src/catalog.rs +++ b/native/sidekit-spotify/src/catalog.rs @@ -2,7 +2,7 @@ //! //! The public Web API has a shared quota that is exhausted by ordinary desktop-client //! credentials. The first-party endpoints used by the Spotify clients do not share that quota, so -//! Sidetracks reads its library, search, artist and collection data through them instead. +//! Sideplay reads its library, search, artist and collection data through them instead. use std::collections::HashSet; diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index f3456928..9cdaf3bc 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -1,4 +1,4 @@ -//! JNI surface for Sidetracks. All state lives in `engine`; these functions only translate. +//! JNI surface for Sideplay. All state lives in `engine`; these functions only translate. mod audio; mod catalog; From 0be9e427f66e798b29a517f3e49ade18c4a6eedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 14:38:44 +0100 Subject: [PATCH 32/46] feat(sideplay): settle parity ownership decisions --- .../sidekit/apps/spotify/SpotifyGraph.kt | 5 ++ .../sidekit/apps/spotify/SpotifyViewModel.kt | 42 +++++++++++----- .../sidekit/apps/spotify/auth/SpotifyAuth.kt | 2 +- .../apps/spotify/data/NativeSpotifyCatalog.kt | 3 -- .../apps/spotify/data/SpotifyCatalog.kt | 1 - .../spotify/data/SpotifyQueueRepository.kt | 25 +++++++--- .../apps/spotify/data/SpotifyWebApi.kt | 11 ----- .../apps/spotify/data/SpotifyWebCatalog.kt | 2 - .../apps/spotify/playback/SpotifyPlayback.kt | 3 ++ .../apps/spotify/MockSpotifyDataSource.kt | 2 - .../sidekit/apps/spotify/SideplayLinksTest.kt | 3 ++ .../spotify/SpotifyQueueRepositoryTest.kt | 22 +++++++++ .../interactive/sidekit/ui/KeypadKeysTest.kt | 6 ++- docs/spotify/SIDEPLAY.md | 40 +++++++-------- native/sidekit-spotify/src/catalog.rs | 49 ++++++++++++------- 15 files changed, 136 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index ec677634..cb11df56 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -57,6 +57,11 @@ object SideplayLinks { return true } + /** Test seam: a unit test must not leave a pending navigation for the next activity. */ + internal fun reset() { + pending.value = false + } + @Composable fun Follow(nav: NavHostController) { val open by pending.collectAsState() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index b5cf5450..230f1aa2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -498,9 +498,9 @@ class SpotifyViewModel( } } - fun toggleShuffle() = applyQueue(queue.toggleShuffle()) + fun toggleShuffle() = applyQueue(queue.toggleShuffle(), syncNowPlaying = false) - fun cycleRepeat() = applyQueue(queue.cycleRepeat()) + fun cycleRepeat() = applyQueue(queue.cycleRepeat(), syncNowPlaying = false) fun toggleSaveCurrent() { val item = _state.value.current ?: return @@ -690,7 +690,7 @@ class SpotifyViewModel( fun clearHistory() { history.clear() val snapshot = queue.clearHistory() - applyQueue(snapshot) + applyQueue(snapshot, syncNowPlaying = false) _state.update { it.copy(recent = emptyList(), notice = "Listening history cleared") } } @@ -700,17 +700,17 @@ class SpotifyViewModel( } fun moveToNext(item: SpotifyMediaItem) { - applyQueue(queue.playNext(item)) + applyQueue(queue.playNext(item), syncNowPlaying = false) } fun removeUpcoming(item: SpotifyMediaItem) { - applyQueue(queue.removeUpcoming(item)) + applyQueue(queue.removeUpcoming(item), syncNowPlaying = false) } fun removeHistory(item: SpotifyMediaItem) { history.remove(item) val snapshot = queue.removeHistory(item) - applyQueue(snapshot) + applyQueue(snapshot, syncNowPlaying = false) _state.update { it.copy( recent = history.items().filterIsInstance(), @@ -720,16 +720,16 @@ class SpotifyViewModel( } fun addToQueue(item: SpotifyMediaItem) { - applyQueue(queue.addToQueue(item)) + applyQueue(queue.addToQueue(item), syncNowPlaying = false) } fun clearUpcoming(): List { val removed = queue.clearUpcoming() - applyQueue(queue.snapshot) + applyQueue(queue.snapshot, syncNowPlaying = false) return removed } - fun restoreUpcoming(items: List) = applyQueue(queue.restoreUpcoming(items)) + fun restoreUpcoming(items: List) = applyQueue(queue.restoreUpcoming(items), syncNowPlaying = false) fun jumpTo(id: String) { val s = _state.value @@ -781,11 +781,11 @@ class SpotifyViewModel( viewModelScope.launch { try { val filter = _state.value.filter - val recent = catalog.recentlyPlayed() val library = catalog.library(filter, 0, SPOTIFY_PAGE_SIZE) _state.update { it.withLibrarySaved(filter, library.items).copy( - recent = recent.ifEmpty { history.items().filterIsInstance() }, + // Recently played is local-only: no first-party history endpoint, no Web API quota. + recent = history.items().filterIsInstance(), libraryItems = library.items, libraryHasMore = library.hasMore, libraryMorePending = false, @@ -913,7 +913,8 @@ class SpotifyViewModel( private fun applyQueue( snapshot: SpotifyQueueRepository.Snapshot, positionMs: Long? = null, - playing: Boolean? = null + playing: Boolean? = null, + syncNowPlaying: Boolean = true ) { _state.update { it.copy( @@ -926,7 +927,9 @@ class SpotifyViewModel( isPlaying = playing ?: it.isPlaying ) } - session?.playback?.setNowPlaying(snapshot.current, positionMs ?: _state.value.positionMs) + if (syncNowPlaying) { + session?.playback?.setNowPlaying(snapshot.current, positionMs ?: _state.value.positionMs) + } } private fun recorded(item: SpotifyMediaItem) { @@ -1050,6 +1053,19 @@ class SpotifyViewModel( "position" -> _state.update { it.copy(positionMs = position) } + // librespot 0.8 has no shuffle/repeat setters, but it emits these when another + // client changes the player. Adopt them so the UI does not drift from the service. + "shuffle" -> applyQueue(queue.setShuffle(event.optBoolean("shuffle")), syncNowPlaying = false) + + "repeat" -> { + val mode = when { + event.optBoolean("track") -> RepeatMode.Track + event.optBoolean("context") -> RepeatMode.Context + else -> RepeatMode.Off + } + applyQueue(queue.setRepeat(mode), syncNowPlaying = false) + } + "ended" -> if (matchesCurrent(event)) onPlaybackEnded() "stopped" -> if (matchesCurrent(event)) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt index b286612e..4dc80cfa 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/auth/SpotifyAuth.kt @@ -154,7 +154,7 @@ class SpotifyAuth( "user-modify-playback-state user-modify-private user-personalized " + "user-read-birthdate user-read-currently-playing user-read-email " + "user-read-play-history user-read-playback-position " + - "user-read-playback-state user-read-private user-read-recently-played " + + "user-read-playback-state user-read-private " + "user-top-read" } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt index 9fbe8f39..94a5d373 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyCatalog.kt @@ -29,9 +29,6 @@ class NativeSpotifyCatalog( private val ensureSession: suspend () -> Unit ) : SpotifyCatalog, SpotifyLibraryWriter { - /** The protocol has no safe recently-played endpoint; the ViewModel falls back to local history. */ - override suspend fun recentlyPlayed(): List = emptyList() - override suspend fun library(filter: LibraryFilter, offset: Int, limit: Int): SpotifyPage { val array = parseArray(callText { native.catalogLibrary(filter.name, offset.toLong(), limit.toLong()) }) val items = when (filter) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt index bc2b34dd..29077a10 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyCatalog.kt @@ -16,7 +16,6 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyTrack * API; tests supply their own catalog. No screen or ViewModel may call an endpoint directly. */ interface SpotifyCatalog { - suspend fun recentlyPlayed(): List suspend fun library( filter: LibraryFilter, offset: Int = 0, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt index 99fb07f2..d458db87 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt @@ -5,8 +5,10 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem import kotlin.random.Random /** - * The queue is owned by Sidekit, not librespot: librespot plays one item at a time and this - * repository decides what comes next. + * The queue is owned by Sideplay, not librespot: librespot plays one item at a time and this + * repository decides what comes next. librespot 0.8 exposes shuffle/repeat events but has no + * setters, so this repository is authoritative for both; native events are adopted when another + * client changes them. * * History and upcoming are plain ordered lists. Shuffle is a view over upcoming only, so the * current item and history never move. The repository is deliberately free of Android and @@ -73,16 +75,27 @@ class SpotifyQueueRepository( ) ) - fun toggleShuffle(): Snapshot = if (snapshot.shuffle) { - commit( + fun toggleShuffle(): Snapshot = setShuffle(!snapshot.shuffle) + + /** Adopts a shuffle change from the native player without re-shuffling an already shuffled tail. */ + fun setShuffle(enabled: Boolean): Snapshot = when { + enabled == snapshot.shuffle -> snapshot + + enabled -> commit( + snapshot.copy( + shuffle = true, + upcoming = snapshot.upcoming.shuffled(random), + shuffleBackup = snapshot.upcoming + ) + ) + + else -> commit( snapshot.copy( shuffle = false, upcoming = snapshot.shuffleBackup ?: snapshot.upcoming, shuffleBackup = null ) ) - } else { - commit(snapshot.copy(shuffle = true, upcoming = snapshot.upcoming.shuffled(random), shuffleBackup = snapshot.upcoming)) } fun clearHistory(): Snapshot = commit(snapshot.copy(history = emptyList())) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt index 88e9eda0..cda3043b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebApi.kt @@ -27,16 +27,6 @@ import org.json.JSONObject * screens never learn an endpoint or a field name. */ class SpotifyWebApi(private val accessToken: suspend () -> String?) { - suspend fun recentlyPlayed(limit: Int = 10): List { - val json = get("$ME_RECENT?limit=${limit.coerceIn(1, 50)}", token()) - val items = json.optJSONArray("items") ?: return emptyList() - return buildList(items.length()) { - for (i in 0 until items.length()) { - items.optJSONObject(i)?.optJSONObject("track")?.let(::track)?.let(::add) - } - } - } - suspend fun library( filter: LibraryFilter, offset: Int = 0, @@ -431,7 +421,6 @@ class SpotifyWebApi(private val accessToken: suspend () -> String?) { private companion object { const val API = "https://api.spotify.com/v1" const val SEARCH = "$API/search" - const val ME_RECENT = "$API/me/player/recently-played" const val ME_TRACKS = "$API/me/tracks" const val ME_PLAYLISTS = "$API/me/playlists" const val ME_ALBUMS = "$API/me/albums" diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt index c9a893da..f94a2e82 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyWebCatalog.kt @@ -12,8 +12,6 @@ import hrln.interactive.sidekit.apps.spotify.SpotifyTrack /** [SpotifyCatalog] over the Web API. */ class SpotifyWebCatalog(private val api: SpotifyWebApi) : SpotifyCatalog { - override suspend fun recentlyPlayed(): List = api.recentlyPlayed() - override suspend fun library( filter: LibraryFilter, offset: Int, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index d9d439eb..792c3be5 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -62,6 +62,9 @@ class SpotifyPlayback( * Queue ops normally live in the ViewModel so its screen state stays in sync. When the * ViewModel is gone, [fallbackNext]/[fallbackPrevious] keep the notification and lock-screen * transport working by advancing the process-wide queue instead. + * + * librespot 0.8 has no shuffle/repeat setters. Sideplay's queue repository owns those, and + * native shuffle/repeat events are adopted by the ViewModel when another client changes them. */ var onNext: (() -> Unit)? = null var onPrevious: (() -> Unit)? = null diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt index cb1bd639..7322f963 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/MockSpotifyDataSource.kt @@ -179,8 +179,6 @@ object MockSpotifyDataSource : SpotifyCatalog { nightSignals[5], glassHours[1], coastline[6], roomsWeLeft[5] ) - override suspend fun recentlyPlayed(): List = recentlyPlayed - fun album(id: String): SpotifyAlbum? = albums.firstOrNull { it.id == id } fun playlist(id: String): SpotifyPlaylist? = playlists.firstOrNull { it.id == id } fun show(id: String): SpotifyShow? = shows.firstOrNull { it.id == id } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt index 77472306..a563c637 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayLinksTest.kt @@ -1,6 +1,7 @@ package hrln.interactive.sidekit.apps.spotify import android.content.Intent +import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test @@ -9,6 +10,8 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) class SideplayLinksTest { + @After fun clearPendingNavigation() = SideplayLinks.reset() + @Test fun `the playback notification action asks for now playing`() { assertTrue(SideplayLinks.handle(Intent(SideplayLinks.ACTION_OPEN_NOW_PLAYING))) } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt index cca58b99..f4fe7326 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt @@ -100,6 +100,28 @@ class SpotifyQueueRepositoryTest { assertEquals(original, queue.snapshot.upcoming.map { it.id }) } + @Test fun `adopting the same shuffle state does not reorder the tail`() { + val queue = SpotifyQueueRepository(Random(7)) + queue.play(tracks, 0) + + queue.setShuffle(true) + val shuffled = queue.snapshot.upcoming.map { it.id } + queue.setShuffle(true) + + assertEquals(shuffled, queue.snapshot.upcoming.map { it.id }) + } + + @Test fun `adopting shuffle off restores the original tail`() { + val queue = SpotifyQueueRepository(Random(7)) + queue.play(tracks, 0) + val original = queue.snapshot.upcoming.map { it.id } + + queue.setShuffle(true) + queue.setShuffle(false) + + assertEquals(original, queue.snapshot.upcoming.map { it.id }) + } + @Test fun `repeat off stops at the end of the queue`() { val queue = SpotifyQueueRepository(Random(1)) queue.play(tracks.take(2), 0) diff --git a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt index a143827b..242f18be 100644 --- a/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/ui/KeypadKeysTest.kt @@ -4,6 +4,7 @@ import android.view.KeyEvent import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.hasContentDescription +import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule import androidx.compose.ui.test.onLast import androidx.compose.ui.test.onNodeWithContentDescription @@ -73,8 +74,9 @@ class KeypadKeysTest { @Test fun screensWithoutOptionsSaySo() { // The dial pad has no options until a number is entered. - compose.waitUntilAtLeastOneExists(hasContentDescription("Phonebook"), 5_000) - compose.onAllNodes(hasContentDescription("Phonebook")).onLast().performClick() + val phonebook = hasContentDescription("Phonebook") or hasText("Phonebook") + compose.waitUntilAtLeastOneExists(phonebook, 15_000) + compose.onAllNodes(phonebook).onLast().performClick() compose.onNodeWithContentDescription("Dial pad").performClick() press(KeyEvent.KEYCODE_MENU) compose.onNodeWithText("No options here").assertIsDisplayed() diff --git a/docs/spotify/SIDEPLAY.md b/docs/spotify/SIDEPLAY.md index 31cd5fd0..fa1df55e 100644 --- a/docs/spotify/SIDEPLAY.md +++ b/docs/spotify/SIDEPLAY.md @@ -107,12 +107,15 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac ## 3. Known limitations -1. **No recently-played protocol endpoint.** `NativeSpotifyCatalog.recentlyPlayed()` returns - empty; Home falls back to local history. -2. **Local queue ownership.** librespot plays one item at a time; Sidekit drives queue, shuffle, - and repeat. Queue, queue history, and recently played persist; playback does not resume - automatically after process death. -3. **Playlist owner names.** Followed playlists show the owner's Spotify username. +1. **Recently played is local-only.** There is no safe first-party history endpoint, and the + Web API quota is not worth spending on a non-critical list. Home shows Sideplay's local + history; it is not cross-device. +2. **Sideplay owns queue, shuffle, and repeat.** librespot 0.8 plays one item at a time and has + no shuffle/repeat setters. `SpotifyQueueRepository` is authoritative; native shuffle/repeat + events are adopted when another client changes them. Queue, queue history, and recently + played persist; playback does not resume automatically after process death. +3. **Playlist owner names.** Your own playlists show **You**; other users' playlists show their + Spotify username. Display-name lookup is not worth a per-owner Web API call. --- @@ -130,20 +133,17 @@ cross-cutting refactor. - Run the existing device checklist for lock screen, notification transport, audio-focus hand-off with local Music, and headphone unplug. -### P1 — parity and completeness - -1. **Recently played.** - - Choose a supported first-party history endpoint or keep local history only. - - If the Web API is the only source, make it opt-in and degrade to local history when the - quota is exhausted. -2. **Queue/shuffle/repeat ownership.** - - Decide whether librespot player state or `SpotifyQueueRepository` is authoritative. - - If Sidekit stays authoritative, mirror shuffle/repeat/autoplay into the native player and - add an explicit native "Play next" path; otherwise delete the duplicate local state. - - Acceptance: Now Playing matches what the native player is doing. -3. **Playlist owner names.** Resolve through the authenticated user profile and cache, not on - every collection open. -4. **Test coverage.** +### P1 — parity decisions (settled) + +1. **Recently played: local-only.** No first-party endpoint, no Web API quota. The local history + is the product feature; cross-device history is a later opt-in, not a merge blocker. +2. **Queue/shuffle/repeat: Sideplay owns them.** librespot 0.8 has no setters, so mirroring is + not possible. `SpotifyQueueRepository` is the single authority; native shuffle/repeat events + are adopted when another client changes them. Now Playing follows the queue, and the native + player follows the item Sideplay starts. +3. **Playlist owner names: authenticated identity, no per-owner lookup.** Your playlists show + **You**; other users show their Spotify username. This avoids a Web API call per collection. +4. **Remaining test coverage.** - `NativeSpotifyCatalog` JSON parsing and error mapping (auth, network, malformed response). - `NativeSpotifyCatalog`/`SpotifyViewModel` event state machine, including `stopped`. - Device smoke checklist for sign-in, library filters, search, playback, queue, lock screen, diff --git a/native/sidekit-spotify/src/catalog.rs b/native/sidekit-spotify/src/catalog.rs index 3ca93037..7713d436 100644 --- a/native/sidekit-spotify/src/catalog.rs +++ b/native/sidekit-spotify/src/catalog.rs @@ -239,6 +239,16 @@ fn playlist_owner(playlist: &Playlist) -> (String, Option) { } } +fn owner_display(owner: &str, current_user: &str) -> String { + if owner.is_empty() { + "Spotify".to_string() + } else if owner == current_user { + "You".to_string() + } else { + owner.to_string() + } +} + fn playlist_art(playlist: &Playlist) -> Option { playlist .attributes @@ -248,12 +258,12 @@ fn playlist_art(playlist: &Playlist) -> Option { .map(|size| size.url.clone()) } -fn playlist_json(playlist: &Playlist, tracks: Vec) -> PlaylistJson { +fn playlist_json(playlist: &Playlist, tracks: Vec, current_user: &str) -> PlaylistJson { let (owner, owner_id) = playlist_owner(playlist); PlaylistJson { id: id_of(&playlist.id), name: playlist.name().to_string(), - owner, + owner: owner_display(&owner, current_user), owner_id, art_res: 0, tracks, @@ -688,15 +698,14 @@ fn pf_artist(data: &Value) -> Option { }) } -fn pf_playlist(data: &Value) -> Option { +fn pf_playlist(data: &Value, current_user: &str) -> Option { let uri = data.get("uri")?.as_str()?; let owner = data.pointer("/ownerV2/data"); + let owner_name = owner.map(|o| pf_str(o, "/name")).unwrap_or_default(); Some(PlaylistJson { id: uri.rsplit(':').next().unwrap_or_default().to_string(), name: pf_str(data, "/name"), - owner: owner - .map(|o| pf_str(o, "/name")) - .unwrap_or_else(|| "Spotify".to_string()), + owner: owner_display(&owner_name, current_user), owner_id: owner .and_then(|o| o.get("uri")) .and_then(Value::as_str) @@ -827,7 +836,7 @@ pub async fn search( section_items(&value, "playlists") .iter() .filter_map(|item| item.get("data")) - .filter_map(pf_playlist) + .filter_map(|data| pf_playlist(data, &session.username())) .collect() } else { Vec::new() @@ -1142,17 +1151,10 @@ async fn rootlist_playlists( } else { Some(owner.clone()) }; - let owner = if owner.is_empty() { - "Spotify".to_string() - } else if owner == user { - "You".to_string() - } else { - owner - }; playlists.push(PlaylistJson { id: uri.rsplit(':').next().unwrap_or_default().to_string(), name, - owner, + owner: owner_display(&owner, &user), owner_id, art_res: 0, tracks: Vec::new(), @@ -1483,10 +1485,7 @@ pub async fn collection( let total = (playlist.length.max(0) as usize).max(all_uris.len()); let uris: Vec = all_uris.into_iter().skip(offset).take(limit).collect(); let tracks = fetch_tracks(session, token, &uris, uris.len()).await; - let mut playlist_json = playlist_json(&playlist, tracks); - if playlist_json.owner == session.username() { - playlist_json.owner = "You".to_string(); - } + let playlist_json = playlist_json(&playlist, tracks, &session.username()); let subtitle = format!( "{} · {} tracks", playlist_json.owner, playlist_json.track_count @@ -1654,3 +1653,15 @@ pub async fn top_tracks( let tracks = fetch_tracks(session, token, &uris, uris.len()).await; Ok(serde_json::to_string(&tracks).map_err(|e| e.to_string())?) } + +#[cfg(test)] +mod tests { + use super::owner_display; + + #[test] + fn owner_display_marks_the_signed_in_user() { + assert_eq!(owner_display("alice", "alice"), "You"); + assert_eq!(owner_display("bob", "alice"), "bob"); + assert_eq!(owner_display("", "alice"), "Spotify"); + } +} From 5934aeb4475b03e4523f941b6079a8573a548e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 14:59:07 +0100 Subject: [PATCH 33/46] fix(build): skip the native task when the NDK is unavailable --- app/build.gradle.kts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 67190c32..50b76285 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -118,12 +118,21 @@ val cargo = listOf( File("/usr/local/bin/cargo") ).firstOrNull { it.canExecute() } +// Resolve the NDK only when Cargo is present. CI has no Rust toolchain, and asking Gradle for +// the NDK directory eagerly would fail configuration even though the task is disabled. +val ndk = if (cargo == null) { + null +} else { + runCatching { androidComponents.sdkComponents.ndkDirectory.get().asFile }.getOrNull() +} + val buildSpotifyNative = tasks.register("buildSpotifyNative") { description = "Build libsidekit_spotify.so for arm64-v8a and x86_64." group = "native" - enabled = cargo != null - if (cargo == null) { - logger.warn("Sideplay: cargo is unavailable; libsidekit_spotify.so will not be packaged.") + enabled = cargo != null && ndk != null + when { + cargo == null -> logger.warn("Sideplay: cargo is unavailable; libsidekit_spotify.so will not be packaged.") + ndk == null -> logger.warn("Sideplay: the Android NDK is unavailable; libsidekit_spotify.so will not be packaged.") } workingDir = rootProject.file("native/sidekit-spotify") commandLine( @@ -134,7 +143,7 @@ val buildSpotifyNative = tasks.register("buildSpotifyNative") { "-o", spotifyJniLibs.get().asFile.absolutePath, "build", "--release" ) - environment("ANDROID_NDK_HOME", androidComponents.sdkComponents.ndkDirectory.get().asFile.absolutePath) + if (ndk != null) environment("ANDROID_NDK_HOME", ndk.absolutePath) inputs.dir(rootProject.file("native/sidekit-spotify/src")) inputs.files(rootProject.file("native/sidekit-spotify/Cargo.toml"), rootProject.file("native/sidekit-spotify/Cargo.lock")) outputs.dir(spotifyJniLibs) From 10a4d61a92b2fefd06b89adfa9b70fa5bc16cf35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 20:30:08 +0100 Subject: [PATCH 34/46] fix(sideplay): address code-scanning lint findings - Move Modifier to the first optional parameter in SpotifyIconTile and EmptyMessage, updating call sites to named arguments. - Replace Uri.parse with the KTX String.toUri() extension. --- .../sidekit/apps/spotify/SpotifyComponents.kt | 8 ++++---- .../sidekit/apps/spotify/SpotifyGraph.kt | 8 ++++---- .../apps/spotify/SpotifyLibraryScreens.kt | 6 +++--- .../apps/spotify/SpotifyNowPlayingScreens.kt | 16 ++++++++-------- .../sidekit/apps/spotify/SpotifySearchScreen.kt | 8 ++++---- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index 9c64810c..ec7bc364 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -170,9 +170,9 @@ fun RowScope.SpotifyIconTile( icon: SidekitIcon, label: String, onClick: () -> Unit, + modifier: Modifier = Modifier, tone: SpotifyTileTone = SpotifyTileTone.Neutral, - weight: Float = 1f, - modifier: Modifier = Modifier + weight: Float = 1f ) { val colors = SidekitTheme.colors SidekitIconItem( @@ -250,7 +250,7 @@ fun ArtworkTile(@DrawableRes artRes: Int, label: String, modifier: Modifier = Mo } @Composable -fun EmptyMessage(title: String, hint: String? = null, modifier: Modifier = Modifier) { +fun EmptyMessage(title: String, modifier: Modifier = Modifier, hint: String? = null) { Column( modifier.fillMaxWidth().padding(horizontal = 20.dp, vertical = 18.dp), verticalArrangement = Arrangement.spacedBy(4.dp) @@ -281,7 +281,7 @@ fun PagingFooter(loading: Boolean, onLoadMore: () -> Unit) { LaunchedEffect(loading) { if (!loading) onLoadMore() } - EmptyMessage(if (loading) "Loading more…" else "More", null) + EmptyMessage(if (loading) "Loading more…" else "More", hint = null) } fun withNotice(notice: String?, onDismiss: () -> Unit, actions: List): List = if (notice == null) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index cb11df56..cac3c19a 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -1,7 +1,6 @@ package hrln.interactive.sidekit.apps.spotify import android.content.Intent -import android.net.Uri import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -9,6 +8,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext +import androidx.core.net.toUri import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.initializer @@ -300,9 +300,9 @@ private fun MissingCollectionScreen(onBack: () -> Unit, loading: Boolean = false androidx.compose.ui.Modifier.padding(padding) ) { if (loading) { - EmptyMessage("Loading…", "Fetching from Spotify") + EmptyMessage("Loading…", hint = "Fetching from Spotify") } else { - EmptyMessage("This collection isn't available", "Go back and choose another") + EmptyMessage("This collection isn't available", hint = "Go back and choose another") } } } @@ -316,7 +316,7 @@ private fun NavBackStackEntry.spotifyViewModel(nav: NavHostController): SpotifyV val openBrowser: suspend (String) -> Unit = remember(context) { { url -> context.startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(url)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + Intent(Intent.ACTION_VIEW, url.toUri()).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ) } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 3aa1ee5b..b617a329 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -70,7 +70,7 @@ fun SideplayHomeScreen( } item(key = "recentLabel") { SectionLabel("Recently played") } if (recent.isEmpty()) { - item(key = "empty") { EmptyMessage("Nothing played yet", "Search to find your first track") } + item(key = "empty") { EmptyMessage("Nothing played yet", hint = "Search to find your first track") } } else { itemsIndexed(recent, key = { _, track -> track.id }) { index, track -> SidekitSwipeRow( @@ -141,7 +141,7 @@ fun LibraryScreen( SidekitList(padding, state = listState) { val items = state.libraryItems if (items.isEmpty()) { - item(key = "empty") { EmptyMessage(state.filter.empty, "Search to find something to save") } + item(key = "empty") { EmptyMessage(state.filter.empty, hint = "Search to find something to save") } } itemsIndexed(items, key = { index, item -> "$index:${libraryKey(item)}" }) { index, item -> val modifier = Modifier.listFade(listState, index).then(if (index == 0) Modifier.focusRequester(first) else Modifier) @@ -223,7 +223,7 @@ fun CollectionScreen( if (saved) SidekitIcon.Check else SidekitIcon.Bookmark, if (saved) "Saved" else "Save", onSave, - if (saved) SpotifyTileTone.On else SpotifyTileTone.Neutral + tone = if (saved) SpotifyTileTone.On else SpotifyTileTone.Neutral ) if (collection.artistId != null) { SpotifyIconTile(SidekitIcon.Artist, "Artist", onArtist) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 319d82ca..a4f49f46 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -89,7 +89,7 @@ fun SpotifyNowPlayingScreen( SidekitScaffold(title = null, actions = actions, options = options) { padding -> if (current == null) { Box(Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) { - EmptyMessage("Nothing playing", "Pick something from your library") + EmptyMessage("Nothing playing", hint = "Pick something from your library") } } else { Column( @@ -132,24 +132,24 @@ fun SpotifyNowPlayingScreen( SidekitIcon.Shuffle, if (state.shuffle) "Shuffle on" else "Shuffle off", onShuffle, - if (state.shuffle) SpotifyTileTone.On else SpotifyTileTone.Neutral, - 0.85f + tone = if (state.shuffle) SpotifyTileTone.On else SpotifyTileTone.Neutral, + weight = 0.85f ) SpotifyIconTile(SidekitIcon.SkipPrevious, "Previous", onPrevious, weight = 0.85f) SpotifyIconTile( if (state.isPlaying) SidekitIcon.Pause else SidekitIcon.PlayArrow, if (state.isPlaying) "Pause" else "Play", onPlayPause, - SpotifyTileTone.Play, - 1.2f + tone = SpotifyTileTone.Play, + weight = 1.2f ) SpotifyIconTile(SidekitIcon.SkipNext, "Next", onNext, weight = 0.85f) SpotifyIconTile( SidekitIcon.Undo, state.repeat.label, onRepeat, - if (state.repeat == RepeatMode.Off) SpotifyTileTone.Neutral else SpotifyTileTone.On, - 0.85f + tone = if (state.repeat == RepeatMode.Off) SpotifyTileTone.Neutral else SpotifyTileTone.On, + weight = 0.85f ) } } @@ -223,7 +223,7 @@ fun QueueScreen( SidekitList(padding, state = listState) { val current = state.current if (current == null) { - item(key = "empty") { EmptyMessage("Nothing queued", "Search to find something to play") } + item(key = "empty") { EmptyMessage("Nothing queued", hint = "Search to find something to play") } } else { item(key = "current") { SpotifyMediaRow( diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt index 6701f1ba..7c2d1ca1 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySearchScreen.kt @@ -100,19 +100,19 @@ fun SpotifySearchScreen( SidekitList(padding, state = listState) { when { state.query.isBlank() -> item(key = "idle") { - EmptyMessage("Search your library and Spotify", "Tracks, artists, albums, playlists and shows") + EmptyMessage("Search your library and Spotify", hint = "Tracks, artists, albums, playlists and shows") } state.query.trim().length < 2 -> item(key = "short") { - EmptyMessage("Keep typing", "Type at least two letters to search") + EmptyMessage("Keep typing", hint = "Type at least two letters to search") } state.searchPending && results.isEmpty -> item(key = "loading") { - EmptyMessage("Searching…", "Spotify is looking for matches") + EmptyMessage("Searching…", hint = "Spotify is looking for matches") } results.isEmpty -> item(key = "noResults") { - EmptyMessage("No results for \"${state.query.trim()}\"", "Try another spelling or turn on more scopes in Options") + EmptyMessage("No results for \"${state.query.trim()}\"", hint = "Try another spelling or turn on more scopes in Options") } else -> { From 85f5fa10e14b655a36c35255a25afa247f070345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 22:02:52 +0100 Subject: [PATCH 35/46] fix(sidekit): keep link arrow opt-in when a row also has a secondary value The shared SidekitItem started rendering the link arrow even when a secondary value was present, which would silently add an arrow to existing rows such as Apps and Mail folders. Gate that behind showLinkArrowWithSecondary (default false) and opt in only from Sideplay's settings rows, which were designed with the trailing arrow. --- .../sidekit/apps/spotify/SpotifySettingsScreens.kt | 3 +++ .../hrln/interactive/sidekit/design/components/Item.kt | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index 92c075fe..3d38c017 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -91,6 +91,7 @@ fun SpotifySettingsScreen( secondary = state.audioQuality.label, secondaryToEnd = true, showLinkArrow = true, + showLinkArrowWithSecondary = true, onClick = onAudioQuality ) } @@ -117,6 +118,7 @@ fun SpotifySettingsScreen( secondary = "12 MB", secondaryToEnd = true, showLinkArrow = true, + showLinkArrowWithSecondary = true, onClick = onClearCache ) } @@ -127,6 +129,7 @@ fun SpotifySettingsScreen( secondary = "${state.history.size} tracks", secondaryToEnd = true, showLinkArrow = true, + showLinkArrowWithSecondary = true, onClick = { confirmation = SettingsConfirmation.ClearHistory } ) } diff --git a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt index 04a04698..9c639a01 100644 --- a/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt +++ b/sidekit/src/main/java/hrln/interactive/sidekit/design/components/Item.kt @@ -81,6 +81,11 @@ fun SidekitItem( secondaryToEnd: Boolean = false, icon: SidekitIcon? = null, showLinkArrow: Boolean = false, + /** + * Keep [showLinkArrow] when [secondary] is present too. Off by default so rows that show a + * value keep their existing look; settings rows that open another page opt in. + */ + showLinkArrowWithSecondary: Boolean = false, colour: ItemColour = ItemColour.Primary, titleDecoration: TextDecoration? = null, onClick: (() -> Unit)? = null, @@ -108,8 +113,9 @@ fun SidekitItem( textAlign = if (secondaryToEnd) TextAlign.End else null ) } - // The arrow marks a row that opens another page, whether or not it also shows a value. - if (showLinkArrow) { + // The arrow marks a row that opens another page. With a secondary value it must be + // requested explicitly, so existing rows keep the arrow off. + if (showLinkArrow && (secondary == null || showLinkArrowWithSecondary)) { Box( if (secondary == null) Modifier.weight(1f) else Modifier, contentAlignment = Alignment.CenterEnd From faf6c0c6ac7ba97276a0ee41b4b470356751a145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 22:54:53 +0100 Subject: [PATCH 36/46] fix(sideplay): rebuild a dead librespot session instead of failing every track After an hour or two idle, librespot logs 'Connection to server closed' and marks its session invalid, but the Rust engine kept the Session handle and accepted token updates. The gateway only reconnected on a token change, so playback kept using the dead session: every track failed to fetch an audio key, the decoder hit end-of-stream, and the player emitted Unavailable for each item ('This track isn't available'), while HTTP catalog reads still worked. - engine::set_access_token now reports SESSION_INVALID when Session::is_invalid(), so the Kotlin side can rebuild. - NativeSpotifyGateway revalidates the session on every ensureConnected() instead of returning early when the token is unchanged, and reconnects when validation fails. Catalog session checks do the same. - ensureConnected() reports whether it rebuilt the session; resume() reloads the current item at its position because a rebuild discards the loaded track. Verified on SP01GE260600886: after closing the access point with airplane mode, the next play logs 'Connecting to AP' / 'Authenticated as ...' and loads the track normally. --- .../apps/spotify/data/NativeSpotifyGateway.kt | 38 ++++++++++--------- .../apps/spotify/data/SpotifySession.kt | 18 +++++++-- .../apps/spotify/NativeSpotifyGatewayTest.kt | 11 ++++++ docs/spotify/SIDEPLAY.md | 11 +++++- native/sidekit-spotify/src/engine.rs | 9 ++++- 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt index 363826da..8d0b7ffa 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt @@ -68,25 +68,29 @@ class NativeSpotifyGateway( sessionToken = null } - /** Connects with the current stored token, refreshing it first when needed. */ - suspend fun ensureConnected() { + /** + * Connects with the current stored token, refreshing it first when needed. + * + * `setAccessToken` is called on every attempt, not only when the token changed: librespot can + * lose its access-point connection while the OAuth token is still valid, and a session that + * has gone invalid must be rebuilt or playback fails for every track. The native call is a + * cheap state update, so the extra round trip on each play is worth the liveness check. + */ + suspend fun ensureConnected(): Boolean { val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") if (!connected) { connect(token) - return + return true } - if (token == sessionToken) return - // The OAuth token rotates every hour. Mutate the existing session instead of reconnecting - // unless native code reports that the session itself has gone away. - val updated = withContext(Dispatchers.IO) { native.setAccessToken(token) } - if (updated) { + if (withContext(Dispatchers.IO) { native.setAccessToken(token) }) { sessionToken = token - return + return false } connected = false sessionConnected = false sessionToken = null connect(token) + return true } /** Connects the librespot session without creating an audio player. Used by catalog reads. */ @@ -95,15 +99,13 @@ class NativeSpotifyGateway( val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") if (!sessionConnected) { connectSession(token) - } else if (token != sessionToken) { - if (!native.setAccessToken(token)) { - // The native session is gone; reconnect from scratch rather than keep a stale token. - sessionConnected = false - sessionToken = null - connectSession(token) - } else { - sessionToken = token - } + } else if (!native.setAccessToken(token)) { + // The native session is gone; reconnect from scratch rather than keep a stale token. + sessionConnected = false + sessionToken = null + connectSession(token) + } else { + sessionToken = token } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index cd18e8f5..cc7d99e0 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -110,8 +110,11 @@ class SpotifySession( _signedIn.value = false } - /** Connects librespot with the current access token, creating the one player on first use. */ - suspend fun ensurePlaybackConnected() = nativeGateway.ensureConnected() + /** + * Connects librespot with the current access token, creating the one player on first use. + * Returns true when the native session had to be rebuilt, which discards the loaded track. + */ + suspend fun ensurePlaybackConnected(): Boolean = nativeGateway.ensureConnected() suspend fun play(uri: String, positionMs: Long = 0L) { ensurePlaybackConnected() @@ -119,8 +122,15 @@ class SpotifySession( } suspend fun resume() { - ensurePlaybackConnected() - withContext(Dispatchers.Main.immediate) { playback.resume() } + val rebuilt = ensurePlaybackConnected() + // A rebuilt session starts from an empty native player. Reload the current item instead of + // asking the player to resume a track it no longer has. + val uri = playback.current?.uri + if (rebuilt && uri != null) { + withContext(Dispatchers.Main.immediate) { playback.start(uri, playback.position) } + } else { + withContext(Dispatchers.Main.immediate) { playback.resume() } + } } suspend fun seek(positionMs: Long) = playback.seek(positionMs) diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt index b62c50d4..16896140 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -73,6 +73,17 @@ class NativeSpotifyGatewayTest { assertEquals(1, native.connects) } + @Test fun `dead native session reconnects even when the token has not changed`() = runTest { + val native = FakeNative().apply { setTokenOk = false } + val gateway = NativeSpotifyGateway(native) { "same" } + gateway.connect("same") + + gateway.ensureConnected() + + assertEquals("same", native.token) + assertEquals(2, native.connects) + } + @Test fun `dead native session reconnects from scratch`() = runTest { val native = FakeNative().apply { setTokenOk = false } var token = "first" diff --git a/docs/spotify/SIDEPLAY.md b/docs/spotify/SIDEPLAY.md index fa1df55e..78d99d6a 100644 --- a/docs/spotify/SIDEPLAY.md +++ b/docs/spotify/SIDEPLAY.md @@ -79,8 +79,11 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac cannot leave stale UI or restore the stopped track. - Sideplay and local Music pause each other through `MediaSessions`; Sideplay takes and abandons `AUDIOFOCUS_GAIN`, including transient loss and headphone changes. -- OAuth tokens rotate into an existing native session. Sign-out stops the decoder synchronously, - releases the media session, removes the notification, then clears credentials. +- OAuth tokens rotate into an existing native session. Every play and resume revalidates that + session: librespot marks it invalid when the access-point connection or its keep-alive dies, + so an invalid session is rebuilt from the stored token and the current item reloads at its + position. Sign-out stops the decoder synchronously, releases the media session, removes the + notification, then clears credentials. - Audio quality persists encrypted across process death and maps to 96/160/320 kbps. Playback volume is the device output volume; there is no separate attenuation. - Artwork is memory-safe on device: 300 px URL preference, `RGB_565` + `inSampleSize`, 1024 px @@ -101,6 +104,10 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac playback advances/pauses correctly, show episodes format dates, and artwork no longer crashes. - On `SP01GE260600886`: instrumented tests cover the foreground-service notification and actions, media-session metadata/state, media-owner hand-off, and the headphone-unplug pause path. +- On `SP01GE260600886`: dropping the network long enough to close the librespot access point + leaves libraries browsable (HTTP catalog) but makes playback fail until the session is rebuilt. + After the fix, the next play reconnects (`Connecting to AP` / `Authenticated as …`) and loads + the track instead of reporting every item as unavailable. - JVM tests cover catalog page offsets, collection totals, and page boundaries. --- diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index 9d24f333..0b02e281 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -141,8 +141,15 @@ pub fn connect(access_token: &str) -> Result<(), String> { pub fn set_access_token(access_token: &str) -> Result<(), String> { let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; - if guard.session.is_none() { + let Some(session) = guard.session.as_ref() else { return Err("SESSION_NOT_CONNECTED".to_string()); + }; + // librespot marks the session invalid when the access point connection closes or the + // keep-alive times out. The stored `Session` handle stays usable, so without this check a + // dead session would accept token updates forever and every later track would load with no + // audio key. Reporting the invalid state lets the Kotlin side reconnect from scratch. + if session.is_invalid() { + return Err("SESSION_INVALID".to_string()); } guard.access_token = Some(access_token.to_string()); Ok(()) From 95c1d0e661e4415f2681b8bed1274153239b2557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 23:29:35 +0100 Subject: [PATCH 37/46] fix(sideplay): bound session reconnects and unblock teardown A dead librespot session was already rebuilt on the next play, but if the access point had stalled, Session::shutdown could block behind a component mutex while the engine held its global STATE lock. That wedged every native call, so the play button hung with no error. - bound the native connect handshake (20s) and the Kotlin wrapper (25s) - take player/session out of STATE and release the lock before stopping the player or shutting the session down - run the old session's shutdown on a detached thread so reconnect is not blocked by a teardown that may never return --- .../apps/spotify/data/SpotifySession.kt | 14 +++++- docs/spotify/SIDEPLAY.md | 8 +++- native/sidekit-spotify/src/engine.rs | 45 ++++++++++++++----- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index cc7d99e0..9a4f6d9d 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -19,6 +19,7 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.coroutines.withTimeoutOrNull /** * Process-wide Spotify identity. It owns the credential store and token refresh, not the UI; the @@ -116,13 +117,17 @@ class SpotifySession( */ suspend fun ensurePlaybackConnected(): Boolean = nativeGateway.ensureConnected() + /** Bounded so a stalled access-point handshake cannot leave the play button stuck forever. */ + private suspend fun connectForPlayback(): Boolean = withTimeoutOrNull(CONNECT_TIMEOUT_MS) { nativeGateway.ensureConnected() } + ?: throw SpotifyGatewayException(SpotifyFailure.NotConnected, "Playback connection timed out") + suspend fun play(uri: String, positionMs: Long = 0L) { - ensurePlaybackConnected() + connectForPlayback() withContext(Dispatchers.Main.immediate) { playback.start(uri, positionMs) } } suspend fun resume() { - val rebuilt = ensurePlaybackConnected() + val rebuilt = connectForPlayback() // A rebuilt session starts from an empty native player. Reload the current item instead of // asking the player to resume a track it no longer has. val uri = playback.current?.uri @@ -197,6 +202,11 @@ class SpotifySession( /** One librespot player event, or null when nothing has happened since the last poll. */ fun pollPlaybackEvent(): String? = if (nativeAvailable) NativeBridge.pollEvent() else null + private companion object { + /** Slightly longer than the native connect timeout, so its specific error wins. */ + const val CONNECT_TIMEOUT_MS = 25_000L + } + private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { override fun read(): hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials? = null override fun save(credentials: hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentials) = Unit diff --git a/docs/spotify/SIDEPLAY.md b/docs/spotify/SIDEPLAY.md index 78d99d6a..22dec2a7 100644 --- a/docs/spotify/SIDEPLAY.md +++ b/docs/spotify/SIDEPLAY.md @@ -82,8 +82,12 @@ lyrics/Canvas/video, social features, account management, crossfade/EQ, and trac - OAuth tokens rotate into an existing native session. Every play and resume revalidates that session: librespot marks it invalid when the access-point connection or its keep-alive dies, so an invalid session is rebuilt from the stored token and the current item reloads at its - position. Sign-out stops the decoder synchronously, releases the media session, removes the - notification, then clears credentials. + position. A connection attempt is bounded (20 s native, 25 s Kotlin) so a stalled handshake + reports an error instead of leaving the play button hanging. Tearing down the old session runs + off the native state lock, because `Session::shutdown` can block behind a stalled network task; + a shutdown that cannot complete leaks one thread instead of wedging playback. Sign-out stops + the decoder synchronously, releases the media session, removes the notification, then clears + credentials. - Audio quality persists encrypted across process death and maps to 96/160/320 kbps. Playback volume is the device output volume; there is no separate attenuation. - Artwork is memory-safe on device: 300 px URL preference, `RGB_565` + `inSampleSize`, 1024 px diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index 0b02e281..e65d08d8 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -45,6 +45,9 @@ impl Default for State { } } +/// Upper bound for one librespot access-point connection attempt. +const CONNECT_TIMEOUT: Duration = Duration::from_secs(20); + static RUNTIME: OnceLock = OnceLock::new(); static STATE: OnceLock> = OnceLock::new(); static AUDIO_RX: OnceLock>>> = OnceLock::new(); @@ -130,8 +133,15 @@ pub fn connect(access_token: &str) -> Result<(), String> { ..Default::default() }; let session = Session::new(config, None); - rt.block_on(session.connect(Credentials::with_access_token(access_token), true)) - .map_err(|e| format!("SESSION_CONNECT_FAILED: {e}"))?; + // A fresh TCP handshake has no deadline of its own, and `Session::connect` may walk up to six + // access points. Bound it so a bad network cannot wedge the caller (and the UI waiting on it) + // indefinitely; the caller retries with a new session on the next play. + let connect = session.connect(Credentials::with_access_token(access_token), true); + match rt.block_on(tokio::time::timeout(CONNECT_TIMEOUT, connect)) { + Ok(Ok(())) => {} + Ok(Err(e)) => return Err(format!("SESSION_CONNECT_FAILED: {e}")), + Err(_) => return Err("SESSION_CONNECT_TIMEOUT".to_string()), + } let mut guard = state().lock().map_err(|_| "STATE_LOCK".to_string())?; guard.session = Some(session); @@ -156,15 +166,30 @@ pub fn set_access_token(access_token: &str) -> Result<(), String> { } pub fn disconnect() { - if let Ok(mut guard) = state().lock() { - if let Some(player) = guard.player.take() { - player.stop(); - } - guard.events = None; - guard.access_token = None; - if let Some(session) = guard.session.take() { - session.shutdown(); + // Take the old handles out and release `STATE` before touching them. `Session::shutdown` + // waits on librespot's component mutexes; when the access-point connection has stalled, a + // network task can be holding one of them, so the call may not return. Holding `STATE` + // across it would wedge every native call (including the reconnect that follows). + let (player, session) = match state().lock() { + Ok(mut guard) => { + let player = guard.player.take(); + let session = guard.session.take(); + guard.events = None; + guard.access_token = None; + (player, session) } + Err(_) => return, + }; + + if let Some(player) = player { + player.stop(); + } + + if let Some(session) = session { + // Reconnect must not wait for the dead session's teardown. If the shutdown cannot + // complete it leaks one thread for the lifetime of the process; that is preferable to + // blocking playback until the app is restarted. + std::thread::spawn(move || session.shutdown()); } } From 0a4654c35136e7438a8b9cd689ad79cf64b64bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 10:28:39 +0100 Subject: [PATCH 38/46] fix(sidetracks): prevent duplicate lazy keys in Sideplay lists Home and Artist render Spotify items with the media id as the LazyColumn key. Older history files could contain the same id twice, and artist album pages can overlap because the native catalog chains albums_current with singles_current. Either case throws IllegalArgumentException during the draw pass. Deduplicate history on load, deduplicate artist albums and tracks on first load and when paging, and prefix the remaining raw-id keys with the row index so a future duplicate cannot crash the list. --- .../sidekit/apps/spotify/SpotifyLibraryScreens.kt | 6 +++--- .../sidekit/apps/spotify/SpotifyViewModel.kt | 8 ++++---- .../apps/spotify/data/SpotifyHistoryRepository.kt | 8 +++++++- .../apps/spotify/SpotifyHistoryRepositoryTest.kt | 11 +++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt index 2b8055cc..d9f8c2bd 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyLibraryScreens.kt @@ -72,7 +72,7 @@ fun SidetracksHomeScreen( if (recent.isEmpty()) { item(key = "empty") { EmptyMessage("Nothing played yet", "Search to find your first track") } } else { - itemsIndexed(recent, key = { _, track -> track.id }) { index, track -> + itemsIndexed(recent, key = { index, track -> "$index:${track.id}" }) { index, track -> SidekitSwipeRow( key = track.id, group = swipes, @@ -306,7 +306,7 @@ fun ArtistScreen( } } item(key = "topLabel") { SectionLabel("Top tracks") } - itemsIndexed(tracks, key = { _, track -> track.id }) { index, track -> + itemsIndexed(tracks, key = { index, track -> "$index:${track.id}" }) { index, track -> SidekitSwipeRow( key = track.id, group = swipes, @@ -320,7 +320,7 @@ fun ArtistScreen( item(key = "tracksMore") { PagingFooter(loadingMore, onLoadMoreTracks) } } item(key = "albumsLabel") { SectionLabel("Albums") } - itemsIndexed(albums, key = { _, album -> album.id }) { index, album -> + itemsIndexed(albums, key = { index, album -> "$index:${album.id}" }) { index, album -> SpotifyLibraryRow( album.artRes, album.title, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 7c1ae7a9..4cf7b39b 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -271,7 +271,7 @@ class SpotifyViewModel( val page = catalog.albumsBy(artist, snapshot.artistAlbums[id].orEmpty().size, SPOTIFY_PAGE_SIZE) _state.update { it.copy( - artistAlbums = it.artistAlbums + (id to it.artistAlbums[id].orEmpty() + page.items), + artistAlbums = it.artistAlbums + (id to (it.artistAlbums[id].orEmpty() + page.items).distinctBy { album -> album.id }), artistAlbumHasMore = it.artistAlbumHasMore + (id to page.hasMore), artistMorePending = it.artistMorePending - id ) @@ -300,7 +300,7 @@ class SpotifyViewModel( val page = catalog.topTracks(artist, snapshot.artistTracks[id].orEmpty().size, SPOTIFY_PAGE_SIZE) _state.update { it.copy( - artistTracks = it.artistTracks + (id to it.artistTracks[id].orEmpty() + page.items), + artistTracks = it.artistTracks + (id to (it.artistTracks[id].orEmpty() + page.items).distinctBy { track -> track.id }), artistTrackHasMore = it.artistTrackHasMore + (id to page.hasMore), artistMorePending = it.artistMorePending - id ) @@ -358,8 +358,8 @@ class SpotifyViewModel( } it.copy( artists = it.artists + (id to artist), - artistAlbums = it.artistAlbums + (id to albums.items), - artistTracks = it.artistTracks + (id to tracks.items), + artistAlbums = it.artistAlbums + (id to albums.items.distinctBy { album -> album.id }), + artistTracks = it.artistTracks + (id to tracks.items.distinctBy { track -> track.id }), artistAlbumHasMore = it.artistAlbumHasMore + (id to albums.hasMore), artistTrackHasMore = it.artistTrackHasMore + (id to tracks.hasMore), followedArtistIds = ids, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt index 095a3835..cb2a86f2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyHistoryRepository.kt @@ -23,7 +23,13 @@ class SpotifyHistoryRepository( private val entries = ArrayDeque() init { - load().take(limit).forEach(entries::addLast) + // Older builds could persist the same item more than once. Home renders this list with + // the item id as its LazyColumn key, so a duplicate would crash the draw pass. + val seen = HashSet() + load().asSequence() + .filter { seen.add(it.item.id) } + .take(limit) + .forEach(entries::addLast) } fun snapshot(): List = entries.toList() diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt index 10e8133b..bc995efa 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyHistoryRepositoryTest.kt @@ -48,6 +48,17 @@ class SpotifyHistoryRepositoryTest { assertEquals(listOf("a", "b"), history.items().map { it.id }) } + @Test fun `load drops duplicate ids from older files`() { + val stored = listOf( + SpotifyHistoryEntry(track("a"), 3), + SpotifyHistoryEntry(track("b"), 2), + SpotifyHistoryEntry(track("a"), 1) + ) + val history = SpotifyHistoryRepository(load = { stored }) + + assertEquals(listOf("a", "b"), history.items().map { it.id }) + } + private fun track(id: String) = SpotifyTrack( id = id, title = "Track $id", From 90cd423b7892f02218ad4991082c6afdcf85adc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 10:59:39 +0100 Subject: [PATCH 39/46] chore: keep screenshots out of the PR Share the Item API with #42 so the two branches merge cleanly. --- app/screenshots/sideplay_album.png | Bin 41356 -> 0 bytes app/screenshots/sideplay_artist.png | Bin 50204 -> 0 bytes app/screenshots/sideplay_home.png | Bin 51202 -> 0 bytes app/screenshots/sideplay_library_albums.png | Bin 52131 -> 0 bytes app/screenshots/sideplay_now_playing.png | Bin 51838 -> 0 bytes app/screenshots/sideplay_queue.png | Bin 50727 -> 0 bytes app/screenshots/sideplay_search.png | Bin 45126 -> 0 bytes app/screenshots/sideplay_settings.png | Bin 30754 -> 0 bytes app/screenshots/sideplay_sign_in.png | Bin 33435 -> 0 bytes .../sidekit/design/components/Item.kt | 31 ++++++++---------- 10 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 app/screenshots/sideplay_album.png delete mode 100644 app/screenshots/sideplay_artist.png delete mode 100644 app/screenshots/sideplay_home.png delete mode 100644 app/screenshots/sideplay_library_albums.png delete mode 100644 app/screenshots/sideplay_now_playing.png delete mode 100644 app/screenshots/sideplay_queue.png delete mode 100644 app/screenshots/sideplay_search.png delete mode 100644 app/screenshots/sideplay_settings.png delete mode 100644 app/screenshots/sideplay_sign_in.png diff --git a/app/screenshots/sideplay_album.png b/app/screenshots/sideplay_album.png deleted file mode 100644 index fd44f15b6c763893171bb9a97878438b87015ae6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41356 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 DUVzwb diff --git a/app/screenshots/sideplay_artist.png b/app/screenshots/sideplay_artist.png deleted file mode 100644 index 5d7f5c94a42a88a18f1cc8d3435b6009a955fa8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50204 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 diff --git a/app/screenshots/sideplay_home.png b/app/screenshots/sideplay_home.png deleted file mode 100644 index 737f9f8366331ffa980fcd080dbc5b53f3d8770f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51202 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ℜ>vX<87LZQ-{W-fL@4(LlhZ#s=4n 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%0D85rGx-iVpkqY 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 O$x14|ED<;S{C@z&&0jVE diff --git a/app/screenshots/sideplay_queue.png b/app/screenshots/sideplay_queue.png deleted file mode 100644 index bddfb4d776881ab865da473b3bea5021b269c707..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50727 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�UklI$5kH6?WuE1AZLT3M6)PYlBVy z^(fwK#e|Wc64^xbyM1fSIp~JK80c+AIw#^ z_=kAIS|p|PIe}D|TsgV$g|CM^gMnfM&*;jpg2kR9(z+2R8G3Fm z8@ve<=uVqYDt=DT_GmjGd)es=Fc}&EvvKb5G?(S3116v}kSB?133A+vfPQzbA+olJ14}pSG2~9OdQ3 z>0|X8wxHB9%P!Ck7%rF%D|=n|#R*_pRLl{fcg|TApqzFh<3T`@ymtr3rr%!Eb%erS z?S`BfY=@j=7BnZ7F$7A3*dG_TPQV_8N$IO17^2bor>{j<&*@dB#jBzX8?uw;FoY8& zQtIm*dF}3n*Dl3(<51og$7W-XUqqpudfyDYKG2=bAe_Z`{_&&KK}VOqQUwFwgGPDY z2m~mKI60O&@$mMHgMlU(b+$~(P$syJg~pmJnzdz(tX!}g3y*zSrX9Leq(Bo+lK;1 zi}tZc=}SP)2*4%Hy&TVQ=~GocGN+f^p)O@sXuiWh8fb+zOLsVz1H2k9=~tvebfj~F-yrwk$esj67Qj7GPA(A#O z9&8MHN$34%FR}m={Bqdqw+toAxj{IB0r*b<0fg*SaqdC!SwEmY&2Cb7A$~W(%X*X2 z2a~(w1&GqgdLob=4{wbRn}buyb>6$H-MS+rBe8qC3Ca8ZWIol8yIm8x+0qKkZok7D z=0}L!5$W^HNL3j8{8Fn1*sr@>jZm6Kqs2?tqgrJVjZs)H#P7afRBDsW>jtFBOxF7~ z_)<^Qjir9g1eeF=Cv*Dakj}{DV?5P*ORco%{Oz7yWGB-EKn;zCs`b$|7GaRAn04Pi zz@P#R{}?E*8xr~dy;^!W4~m!o4HKiafLezi>T7=`Teewh7Zhq9s)l{y@sESYp@e~a zF469KMJ&jhsnO$BL^|#|4>R3qE{mwIfY=BK2UVRB{1^;k)J7%n2-@#_Zv2-8MXKxS z(6Y0$F-6#z9m_GrIo%#@fg7LQl5B<;+wh)l%@p~4rr!k;(ff6Qmd^kD#LAVgm2Vy+CP2*g0dxi2oQziUy znE)Z_%o|GVl16%MhwG-EeoXP=H}<=NV)G%XJ)Y4CxQVOt|OLILUHon2lg)K)=Yjwau^RQ9S4``Vsw(V>^hFOPcTdS4cYdC=fK z#+hNsNr&AaB7~rVY8NJwg;Vpjb(GAFnbTckCrBl|0!47Ne^EA8eQ|N|2a2%0!FptU zjaG*EYP7|9Bil6KVn<@2XO!i2*drW;^e=oK21peixL%r3_tFWl)9zaxXn=00=R$Wa z#yqR3ZoS5VA9M)CTtmk{|{Xxa@c(Oz>CVx_F zrq5*k<3WNPG;EOyo|yfx=!~_CYPgfi4Xf;r-02B+7c$VOCKh997nG9r$Um@NscIB;;IKBOL0sTyLyG)O`rDPvGdj$D5j^* zX61$BD5B6I#f+Xj3-asmJVe*(8|(^@UilU2dq7c+6MylWq|M>!M=EtQ=BM=UPD3!Y z(LT7u@lTWL#~c{gVWYAo7#{kgQg^lJ+SWfZhcqijk38l3X6Ge6txte`;OhM2kAEx? z*15@1cY^QG>=v5sU2^zx4VGcdbDi$s*z?-9;NW5!V;icq2`!Q}g?JW@sX|5tv|7~E zWINpJNY9S?G+!0wBacD%a;@b5OO`S@&R;+u(g)71&^V1KCz*X-lIZ3>(spQ|8iPBS zK!ff^!gs!yVKF@KyJ}m9N~b=r@r?G~LkUSO0GsCL&(M9gqddp?EX2GJ-6%mbTe`;0 zy4i^YS}`sH30w{^T!riD-x0F(2#YMAHCwuFpF-8V)_ z-K>x`VI)1@rT%fTM%F<5z$D1F)9%XTAT1iY-Sg z7gc5gdc_c!S}vJG+&#Rd@7Ta+m&%r_$&ee9LMc&}spVBnk32D^vJX!PH_R#HFB5J> zf`G1+)?2!LBMw{UWK*1{bRJ1sjN4tdyT%x7E4aXuLi>gycxO)7=1SS<^9zPG;|!tu ziVBcdw5T6F29jIxVqasP@f4Jy0TCeSdy$KdE&SR7Tzqn=Pi3FQon&sNJt9x|S>INE zt|_ijT^CS?vAXB{Ieik5Jk*ech{|V}BqMAA;1H|wm2>kbFUYFfu~M--RM3Y=z6p!7GZ|RuUbVyAkl)1?PT>jWfTQOcXou>@ioo`e=Ug^n5?Wu_>`0FMVP*<>ce1I0Q$aE zY-t;`4m{NGk|aZ@y4+f}|08|;l&p6w(IVzrfsq5 z7OYe4Y0yq%Rk}MN9_WeIdK|-tm diff --git a/app/screenshots/sideplay_search.png b/app/screenshots/sideplay_search.png deleted file mode 100644 index 43f58192a17c214e37af3f7d46b16f2af71167e8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45126 zcmeFZXH-*N+b&8d5_*#&Rf+=Am0lH)qEbYq_aY#@2SSNb1t|&$N-v6tbO<#N=^#x| z5?bg*TBwQiz4AQY`|UmUI6uxfdyMnv9RCQ(WX(D6KJR&5>$Rc2HVq{^B@q!3jjoQS z5fKq+o`{GT2EGD(qS;pld;lZT)x7rKlr2Q)pdf8BNSiKtAlwt=*TjG|4WIk z+5*276C1)Pc%xD%?}Bau!)_7%|6l%}2|+%lmnUN8^tAmFik-VWpF?0n*OaWawPRCb z0jUdLrSv9cm9Rd%z+PUA)J9-J5A+0WZXl^%o*(Q4wNLkJfO#DcG(bu77IP=YI?KBU zUY9s8{k8`L^`vZr$mEGBB>{CM#t?IU4?tP1QQy-@3ut>_=n3+4b=^}vog9f_V`ul> zXHW<0l9S2Dl;nj^o&GV1ctMJ7nLDpQ{rpa3O$OAmk-w@Wo zMzDxU?*F?#xr=}*N=x@D5IA#e7#5m`e{B%j0X;uLAq4Y6$CMzu`Kdug-*H^OR#q}> za2UuE4c39@G!s8~X9OZ>kn6wynKzFJSkQ;m)(5}I|CyT&o<`)hH`(BoryGmDHy@6x z&5Pc`jm(`5%|Q&5_oALW=RC1CUti43%DMxMiMTk(i>+vg2tQlmfl43_-v!&HYDTA-GDhvb!rt6^i?YFXA~D zN_)S&R(W|+iO9|6E;+Nr4+tW6kyOXW3v?0dG}P2rMUnW~ARFCN81W-8?~2_+s@=TH zmOFBdNZJYo#It91m*Ct_0X>#6!g z#>PEU`lKW~)F+ipPaJ%r}sgt@{sz=vpUVzornh5f8*-JXkdU z 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/sideplay_library_albums.png b/app/screenshots/sideplay_library_albums.png new file mode 100644 index 0000000000000000000000000000000000000000..3658846557aeae2973bf587206ddc18da906684f GIT binary patch literal 52131 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%0D85rGx-iVpkqY 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 O$x14|ED<;S{C@z&&0jVE literal 0 HcmV?d00001 diff --git a/app/screenshots/sideplay_queue.png b/app/screenshots/sideplay_queue.png new file mode 100644 index 0000000000000000000000000000000000000000..bddfb4d776881ab865da473b3bea5021b269c707 GIT binary patch literal 50727 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�UklI$5kH6?WuE1AZLT3M6)PYlBVy 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/sideplay_search.png b/app/screenshots/sideplay_search.png new file mode 100644 index 0000000000000000000000000000000000000000..43f58192a17c214e37af3f7d46b16f2af71167e8 GIT binary patch literal 45126 zcmeFZXH-*N+b&8d5_*#&Rf+=Am0lH)qEbYq_aY#@2SSNb1t|&$N-v6tbO<#N=^#x| z5?bg*TBwQiz4AQY`|UmUI6uxfdyMnv9RCQ(WX(D6KJR&5>$Rc2HVq{^B@q!3jjoQS z5fKq+o`{GT2EGD(qS;pld;lZT)x7rKlr2Q)pdf8BNSiKtAlwt=*TjG|4WIk z+5*276C1)Pc%xD%?}Bau!)_7%|6l%}2|+%lmnUN8^tAmFik-VWpF?0n*OaWawPRCb z0jUdLrSv9cm9Rd%z+PUA)J9-J5A+0WZXl^%o*(Q4wNLkJfO#DcG(bu77IP=YI?KBU zUY9s8{k8`L^`vZr$mEGBB>{CM#t?IU4?tP1QQy-@3ut>_=n3+4b=^}vog9f_V`ul> zXHW<0l9S2Dl;nj^o&GV1ctMJ7nLDpQ{rpa3O$OAmk-w@Wo zMzDxU?*F?#xr=}*N=x@D5IA#e7#5m`e{B%j0X;uLAq4Y6$CMzu`Kdug-*H^OR#q}> za2UuE4c39@G!s8~X9OZ>kn6wynKzFJSkQ;m)(5}I|CyT&o<`)hH`(BoryGmDHy@6x z&5Pc`jm(`5%|Q&5_oALW=RC1CUti43%DMxMiMTk(i>+vg2tQlmfl43_-v!&HYDTA-GDhvb!rt6^i?YFXA~D zN_)S&R(W|+iO9|6E;+Nr4+tW6kyOXW3v?0dG}P2rMUnW~ARFCN81W-8?~2_+s@=TH zmOFBdNZJYo#It91m*Ct_0X>#6!g z#>PEU`lKW~)F+ipPaJ%r}sgt@{sz=vpUVzornh5f8*-JXkdU z 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 literal 29628 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ƽ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 Unit)? = null, onClickLabel: String? = null, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit, + content: @Composable BoxScope.() -> Unit ) { Box( modifier = modifier @@ -55,13 +55,16 @@ fun SidekitTile( .clip(SidekitTheme.shapes.item) .background(color) .then( - if (onClick != null) Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) - else Modifier, + if (onClick != null) { + Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) + } else { + Modifier + } ), contentAlignment = contentAlignment, content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - }, + } ) } @@ -86,18 +89,18 @@ fun SidekitItem( colour: ItemColour = ItemColour.Primary, titleDecoration: TextDecoration? = null, onClick: (() -> Unit)? = null, - leading: (@Composable RowScope.() -> Unit)? = null, + leading: (@Composable RowScope.() -> Unit)? = null ) { SidekitTile( modifier = modifier.fillMaxWidth(), color = colour.container(), onClick = onClick, - contentAlignment = Alignment.CenterStart, + contentAlignment = Alignment.CenterStart ) { Row( modifier = Modifier.padding(horizontal = SidekitTheme.spacing.itemPadding), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap), + horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap) ) { leading?.invoke(this) if (icon != null) SidekitIconImage(icon) @@ -107,7 +110,7 @@ fun SidekitItem( secondary, Modifier.weight(1f), color = SidekitTheme.colors.surfaceA6, - textAlign = if (secondaryToEnd) TextAlign.End else null, + textAlign = if (secondaryToEnd) TextAlign.End else null ) } // The arrow marks a row that opens another page. With a secondary value it must be @@ -115,7 +118,7 @@ fun SidekitItem( if (showLinkArrow && (secondary == null || showLinkArrowWithSecondary)) { Box( if (secondary == null) Modifier.weight(1f) else Modifier, - contentAlignment = Alignment.CenterEnd, + contentAlignment = Alignment.CenterEnd ) { SidekitIconImage(SidekitIcon.ArrowRightAlt) } @@ -133,7 +136,7 @@ fun SidekitIconItem( colour: ItemColour = ItemColour.Primary, color: Color = colour.container(), iconSize: Dp = SidekitTheme.spacing.iconSize, - onClick: (() -> Unit)? = null, + onClick: (() -> Unit)? = null ) { SidekitTile(modifier = modifier, color = color, onClick = onClick, onClickLabel = contentDescription) { SidekitIconImage(icon, size = iconSize, contentDescription = contentDescription) @@ -146,7 +149,7 @@ fun SidekitItemRow(modifier: Modifier = Modifier, content: @Composable RowScope. Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemOuterGap), - content = content, + content = content ) } @@ -156,7 +159,7 @@ fun SidekitCard( modifier: Modifier = Modifier, color: Color = SidekitTheme.colors.surfaceA3, onClick: (() -> Unit)? = null, - content: @Composable BoxScope.() -> Unit, + content: @Composable BoxScope.() -> Unit ) { Box( modifier = modifier @@ -166,6 +169,6 @@ fun SidekitCard( .padding(SidekitTheme.spacing.itemPadding), content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - }, + } ) } From 916fe56522ccd3a9ffde8d098bfc53ea2b3a42ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 16:22:59 +0100 Subject: [PATCH 45/46] fix(sideplay): align now playing actions with playback --- app/screenshots/sideplay_now_playing.png | Bin 51838 -> 51873 bytes .../sidekit/apps/spotify/SpotifyComponents.kt | 8 ++++++-- .../sidekit/apps/spotify/SpotifyGraph.kt | 5 +++++ .../apps/spotify/SpotifyNowPlayingScreens.kt | 18 +++++++++++++++--- .../apps/spotify/SideplayScreenshotTest.kt | 1 + 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/screenshots/sideplay_now_playing.png b/app/screenshots/sideplay_now_playing.png index 564ef2b415cbeda751dcbe8ae6208d1f8128d961..e332e1935a77ddf00b189b1bb90d3e6fcbf87562 100644 GIT binary patch delta 25385 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 K`aXKgwb|A)A)ddoQdCQ2|C5P}KPdeS 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 Z6c+KP#qz_9cf2t0Q&rSdD3&vS`9Jd@?5h9( diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index adb3db45..cb21dce8 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -44,6 +44,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.ProgressBarRangeInfo import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.semantics @@ -74,7 +75,9 @@ fun SpotifyCover( shape: androidx.compose.ui.graphics.Shape = SidekitTheme.shapes.control, bordered: Boolean = false, contentDescription: String? = null, - artUrl: String? = null + artUrl: String? = null, + onClick: (() -> Unit)? = null, + onClickLabel: String? = null ) { val colors = SidekitTheme.colors Box( @@ -82,6 +85,7 @@ fun SpotifyCover( .clip(shape) .background(colors.surfaceA2) .then(if (bordered) Modifier.border(1.dp, colors.light, shape) else Modifier) + .then(if (onClick != null) Modifier.clickable(onClickLabel = onClickLabel, onClick = onClick) else Modifier) ) { when { artUrl != null -> RemoteArtworkImage( @@ -348,7 +352,7 @@ fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modi val step = if (seekable) 5_000f / durationMs else 0f Row(modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { Box(contentAlignment = Alignment.CenterEnd) { - SidekitText(widest, Modifier.alpha(0f), style = timeStyle) + SidekitText(widest, Modifier.alpha(0f).clearAndSetSemantics {}, style = timeStyle) SidekitText(formatDuration(positionMs), style = timeStyle) } Box( diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt index 37134165..ecf48c90 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyGraph.kt @@ -227,6 +227,11 @@ fun NavGraphBuilder.sideplayGraph(nav: NavHostController) { nav.navigate(collectionRoute(SpotifyCollectionType.Album, track.albumId)) } }, + onShow = { + (state.current as? SpotifyEpisode)?.let { episode -> + nav.navigate(collectionRoute(SpotifyCollectionType.Show, episode.showId)) + } + }, onArtist = { state.current?.artistId?.let { nav.navigate(artistRoute(it)) } }, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index a4f49f46..4a914f2a 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -56,15 +56,17 @@ fun SpotifyNowPlayingScreen( onSave: () -> Unit, onQueue: () -> Unit, onAlbum: () -> Unit, + onShow: () -> Unit, onArtist: () -> Unit, onUndo: () -> Unit, onDismissNotice: () -> Unit ) { val current = state.current - val save = current?.let { + // Episodes have no saved-track endpoint, so Save is a track-only action. + val save = (current as? SpotifyTrack)?.let { SideAction.square( "save", - if (state.savedCurrent) SidekitIcon.Bookmark else SidekitIcon.BookmarkBorder, + if (state.savedCurrent) SidekitIcon.Check else SidekitIcon.Bookmark, if (state.savedCurrent) "Saved" else "Save", onSave, tone = if (state.savedCurrent) ActionTone.Secondary else ActionTone.Muted, @@ -105,7 +107,17 @@ fun SpotifyNowPlayingScreen( shape = SidekitTheme.shapes.item, bordered = true, contentDescription = "${current.title} artwork", - artUrl = current.artUrl + artUrl = current.artUrl, + onClick = when (current) { + is SpotifyTrack -> onAlbum + is SpotifyEpisode -> onShow + else -> null + }, + onClickLabel = when (current) { + is SpotifyTrack -> "Open album" + is SpotifyEpisode -> "Open show" + else -> null + } ) } Column(horizontalAlignment = Alignment.CenterHorizontally) { diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt index 19a1b19d..c00f47f1 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt @@ -143,6 +143,7 @@ class SideplayScreenshotTest { onSave = {}, onQueue = {}, onAlbum = {}, + onShow = {}, onArtist = {}, onUndo = {}, onDismissNotice = {} From 81837beca2cf671184a68643d1b650eab68e0e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 23:29:17 +0100 Subject: [PATCH 46/46] fix(sideplay): stabilise playback, seeking and now playing state - Pace PCM through the AudioTrack hardware clock and drain queued audio before load/pause so a blocked player thread cannot wedge commands. - Serialize AudioTrack lifecycle operations and stop the drain before releasing or flushing, fixing the native crash when seeking. - Keep pause/resume sample-continuous and drive the seek bar from a monotonic clock so it never jumps on pause or resume. - Coalesce drag seeks to one native seek on release and ignore stale decoder position events while a seek settles. - Filter stale playing/paused events by command intent so rapid taps cannot leave the UI showing play with no sound. - Toggle the Save action between hollow and filled bookmarks. --- .../sidekit/apps/spotify/SideplayDebug.kt | 25 +++ .../sidekit/apps/spotify/SpotifyComponents.kt | 34 +++- .../apps/spotify/SpotifyNowPlayingScreens.kt | 2 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 95 ++++++++- .../apps/spotify/bridge/NativeBridge.kt | 21 +- .../apps/spotify/bridge/SpotifyNative.kt | 1 + .../apps/spotify/data/NativeSpotifyGateway.kt | 20 +- .../apps/spotify/data/SpotifySession.kt | 20 +- .../apps/spotify/playback/SpotifyAudioSink.kt | 184 ++++++++++++++++-- .../apps/spotify/playback/SpotifyPlayback.kt | 126 ++++++++++-- native/sidekit-spotify/src/audio.rs | 29 ++- native/sidekit-spotify/src/engine.rs | 22 +++ native/sidekit-spotify/src/lib.rs | 8 + 13 files changed, 538 insertions(+), 49 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/SideplayDebug.kt diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SideplayDebug.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SideplayDebug.kt new file mode 100644 index 00000000..c5eb7880 --- /dev/null +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SideplayDebug.kt @@ -0,0 +1,25 @@ +package hrln.interactive.sidekit.apps.spotify + +import android.util.Log + +/** + * Temporary live-debug logging for the Sideplay playback path. + * + * Enable on device with: + * adb shell setprop log.tag.SideplayDebug DEBUG + * + * The tag is checked at every call, so the property can be toggled without reinstalling. + */ +internal object SideplayDebug { + const val TAG = "SideplayDebug" + + val enabled: Boolean get() = Log.isLoggable(TAG, Log.DEBUG) + + fun log(message: String) { + if (enabled) Log.d(TAG, message) + } + + fun log(message: String, error: Throwable) { + if (enabled) Log.d(TAG, message, error) + } +} diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index cb21dce8..cb16a3b8 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -24,7 +24,10 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha @@ -340,6 +343,12 @@ fun repeatLabel(repeat: RepeatMode) = repeat.label fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modifier: Modifier = Modifier) { val seekable = durationMs > 0 val fraction = if (seekable) (positionMs.toFloat() / durationMs).coerceIn(0f, 1f) else 0f + // While the finger is down the bar follows the finger locally. The player is only asked to + // seek once, on release: issuing a seek for every drag delta floods the native player and + // makes the bar take seconds to settle on the final position. + var dragFraction by remember { mutableStateOf(null) } + val shownFraction = dragFraction ?: fraction + val shownPositionMs = if (dragFraction != null) (shownFraction * durationMs).toLong() else positionMs val trackColor = SidekitTheme.colors.surfaceA4 val progressColor = SidekitTheme.colors.light val labelStyle = SidekitTheme.typography.label @@ -353,7 +362,7 @@ fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modi Row(modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) { Box(contentAlignment = Alignment.CenterEnd) { SidekitText(widest, Modifier.alpha(0f).clearAndSetSemantics {}, style = timeStyle) - SidekitText(formatDuration(positionMs), style = timeStyle) + SidekitText(formatDuration(shownPositionMs), style = timeStyle) } Box( Modifier @@ -378,11 +387,26 @@ fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modi .indication(interaction, LocalIndication.current) .focusable(interactionSource = interaction) .pointerInput(durationMs) { detectTapGestures { offset -> seekTo(offset.x / size.width) } } - .pointerInput(durationMs) { detectHorizontalDragGestures { change, _ -> seekTo(change.position.x / size.width) } } + .pointerInput(durationMs) { + detectHorizontalDragGestures( + onDragStart = { offset -> + if (seekable) dragFraction = (offset.x / size.width).coerceIn(0f, 1f) + }, + onHorizontalDrag = { change, _ -> + if (seekable) dragFraction = (change.position.x / size.width).coerceIn(0f, 1f) + }, + onDragEnd = { + val target = dragFraction + dragFraction = null + if (target != null) seekTo(target) + }, + onDragCancel = { dragFraction = null } + ) + } .semantics { if (seekable) { contentDescription = "Seek" - progressBarRangeInfo = ProgressBarRangeInfo(fraction, 0f..1f) + progressBarRangeInfo = ProgressBarRangeInfo(shownFraction, 0f..1f) setProgress { target -> seekTo(target) true @@ -393,8 +417,8 @@ fun SpotifySeek(positionMs: Long, durationMs: Long, onSeek: (Long) -> Unit, modi val h = 6.dp.toPx() val y = (size.height - h) / 2 drawRoundRect(trackColor, Offset(0f, y), Size(size.width, h), CornerRadius(h / 2)) - if (fraction > 0f) { - val filled = size.width * fraction + if (shownFraction > 0f) { + val filled = size.width * shownFraction drawRoundRect(progressColor, Offset(0f, y), Size(filled, h), CornerRadius(h / 2)) drawCircle(progressColor, 9.dp.toPx(), Offset(filled, size.height / 2)) } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt index 4a914f2a..f9b14bc1 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyNowPlayingScreens.kt @@ -66,7 +66,7 @@ fun SpotifyNowPlayingScreen( val save = (current as? SpotifyTrack)?.let { SideAction.square( "save", - if (state.savedCurrent) SidekitIcon.Check else SidekitIcon.Bookmark, + if (state.savedCurrent) SidekitIcon.Bookmark else SidekitIcon.BookmarkBorder, if (state.savedCurrent) "Saved" else "Save", onSave, tone = if (state.savedCurrent) ActionTone.Secondary else ActionTone.Muted, diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 8b1198c8..f5836e67 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -1,5 +1,6 @@ package hrln.interactive.sidekit.apps.spotify +import android.os.SystemClock import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import hrln.interactive.sidekit.apps.spotify.data.SpotifyCatalog @@ -10,6 +11,7 @@ import hrln.interactive.sidekit.apps.spotify.data.SpotifyLibraryWriter import hrln.interactive.sidekit.apps.spotify.data.SpotifyQueueRepository import hrln.interactive.sidekit.apps.spotify.data.SpotifySession import hrln.interactive.sidekit.ui.UndoWindow +import kotlin.math.abs import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -111,6 +113,15 @@ class SpotifyViewModel( private val previousHandler = { previous() } private val seekHandler = { position: Long -> seekTo(position) } + /** + * Smooth-position anchor. librespot emits position events on its own cadence (and they can + * arrive late), so the UI interpolates from the last known position with the monotonic clock. + * When an AudioTrack exists, its playback head is used instead: that is the real audio clock. + */ + private var positionAnchorMs = 0L + private var positionAnchorAt = 0L + private var lastSeekAt = 0L + init { session?.let { viewModelScope.launch { @@ -147,6 +158,12 @@ class SpotifyViewModel( handlePlaybackEvent(raw) } } + viewModelScope.launch { + while (true) { + delay(100L) + updatePositionTick() + } + } } } @@ -160,6 +177,7 @@ class SpotifyViewModel( queue.restore(snapshot.copy(current = current)) } val restored = queue.snapshot + anchorPosition(playback.position) _state.update { it.copy( current = current, @@ -434,6 +452,7 @@ class SpotifyViewModel( val s = _state.value val item = s.current ?: return val play = !s.isPlaying + anchorPosition(s.positionMs) _state.update { it.copy(isPlaying = play) } if (item.uri == null || session == null) return viewModelScope.launch { @@ -443,7 +462,10 @@ class SpotifyViewModel( // of resuming a source that no longer exists. startRemote(item, 0L) } else if (play) { - session.resume() + if (!session.resume()) { + _state.update { it.copy(isPlaying = false) } + return@launch + } } else { session.pause() } @@ -496,6 +518,8 @@ class SpotifyViewModel( fun seekTo(positionMs: Long) { val current = _state.value.current ?: return val target = positionMs.coerceIn(0L, current.durationMs) + lastSeekAt = SystemClock.elapsedRealtime() + anchorPosition(target) _state.update { it.copy(positionMs = target) } if (current.uri != null && session != null) { viewModelScope.launch { runCatching { session.seek(target) } } @@ -927,6 +951,7 @@ class SpotifyViewModel( playing: Boolean? = null, syncNowPlaying: Boolean = true ) { + if (positionMs != null) anchorPosition(positionMs) _state.update { it.copy( current = snapshot.current, @@ -1047,22 +1072,54 @@ class SpotifyViewModel( private fun handlePlaybackEvent(raw: String) { val event = runCatching { JSONObject(raw) }.getOrNull() ?: return val position = event.optLong("positionMs", _state.value.positionMs) + SideplayDebug.log("event type=${event.optString("type")} uri=${event.optString("uri")} positionMs=$position") when (event.optString("type")) { "loading" -> if (matchesCurrent(event)) { + anchorPosition(position) + session?.playback?.updatePosition(position) _state.update { it.copy(positionMs = position) } } "playing" -> if (matchesCurrent(event)) { - _state.update { it.copy(isPlaying = true, positionMs = position) } - session?.playback?.updateState(true, position) + val playback = session?.playback + // Ignore a playing event that belongs to a command the user already superseded + // with a pause. Otherwise rapid taps leave the UI showing play with no sound. + if (playback?.shouldApplyEvent(true) != false) { + // Keep the UI clock where it is. librespot's event position is the decoder + // position, which is ahead of the speaker by the PCM buffer; adopting it + // would make the bar jump forward on resume. + val current = _state.value.positionMs + anchorPosition(current) + playback?.updateState(true, current) + _state.update { it.copy(isPlaying = true, positionMs = current) } + } } "paused" -> if (matchesCurrent(event)) { - _state.update { it.copy(isPlaying = false, positionMs = position) } - session?.playback?.updateState(false, position) + val playback = session?.playback + // Ignore a paused event that belongs to a command the user already superseded + // with a resume. Otherwise the UI flickers back to paused after a resume. + if (playback?.shouldApplyEvent(false) != false) { + val current = _state.value.positionMs + anchorPosition(current) + playback?.updateState(false, current) + _state.update { it.copy(isPlaying = false, positionMs = current) } + } } - "position" -> _state.update { it.copy(positionMs = position) } + "position" -> { + // The 100 ms ticker owns the seek bar. Only adopt an event position when it is + // far enough away to be a real seek from another client, not the decoder's + // normal lead over the speaker. + val current = _state.value.positionMs + // Ignore the decoder's stale position events while a local seek is settling. + val settling = SystemClock.elapsedRealtime() - lastSeekAt < 1_500L + if (!settling && abs(position - current) > 2_000L) { + anchorPosition(position) + session?.playback?.updatePosition(position) + _state.update { it.copy(positionMs = position) } + } + } // librespot 0.8 has no shuffle/repeat setters, but it emits these when another // client changes the player. Adopt them so the UI does not drift from the service. @@ -1108,6 +1165,28 @@ class SpotifyViewModel( } } + private fun anchorPosition(positionMs: Long) { + positionAnchorMs = positionMs + positionAnchorAt = SystemClock.elapsedRealtime() + } + + /** + * Advances the seek bar from a monotonic clock anchored to the last known position. The + * AudioTrack playback head is not stable across pause/resume on this device, so it is not + * used for the UI; the system clock advances at the same 1.0x rate and never jumps. + */ + private fun updatePositionTick() { + val s = _state.value + if (!s.isPlaying) return + val current = s.current ?: return + val next = positionAnchorMs + (SystemClock.elapsedRealtime() - positionAnchorAt) + val clamped = next.coerceIn(0L, current.durationMs) + if (clamped != s.positionMs) { + session?.playback?.updatePosition(clamped) + _state.update { it.copy(positionMs = clamped) } + } + } + private fun tick() { val s = _state.value val current = s.current ?: return @@ -1197,13 +1276,17 @@ class SpotifyViewModel( private fun startRemote(item: SpotifyMediaItem, positionMs: Long) { val uri = item.uri ?: return val session = session ?: return + SideplayDebug.log("viewModel.startRemote uri=$uri positionMs=$positionMs") + anchorPosition(positionMs) session.playback.setNowPlaying(item, positionMs) viewModelScope.launch { try { session.play(uri, positionMs) + SideplayDebug.log("viewModel.startRemote returned") } catch (e: CancellationException) { throw e } catch (e: Exception) { + SideplayDebug.log("viewModel.startRemote failed", e) _state.update { it.copy(isPlaying = false, notice = playbackNotice(e)) } } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index f0f8d866..eb2de255 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -1,5 +1,6 @@ package hrln.interactive.sidekit.apps.spotify.bridge +import hrln.interactive.sidekit.apps.spotify.SideplayDebug import hrln.interactive.sidekit.system.NativeLibraries /** @@ -31,7 +32,20 @@ object NativeBridge : SpotifyNative { override fun createPlayer(bitrateKbps: Int): Boolean = isAvailable && runCatching { nativeCreatePlayer(bitrateKbps) }.getOrDefault(false) - override fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean = isAvailable && runCatching { nativeLoad(uri, positionMs, autoplay) }.getOrDefault(false) + override fun load(uri: String, positionMs: Long, autoplay: Boolean): Boolean { + if (!isAvailable) { + SideplayDebug.log("native.load unavailable") + return false + } + val started = System.nanoTime() + return runCatching { nativeLoad(uri, positionMs, autoplay) } + .onFailure { SideplayDebug.log("native.load threw", it) } + .getOrDefault(false) + .also { + val elapsedMs = (System.nanoTime() - started) / 1_000_000 + SideplayDebug.log("native.load returned ok=$it elapsedMs=$elapsedMs") + } + } override fun play(): Boolean = isAvailable && runCatching { nativePlay() }.getOrDefault(false) @@ -43,6 +57,10 @@ object NativeBridge : SpotifyNative { override fun readAudio(timeoutMs: Long): ByteArray? = if (isAvailable) runCatching { nativeReadAudio(timeoutMs) }.getOrNull() else null + override fun drainAudio() { + if (isAvailable) runCatching { nativeDrainAudio() } + } + override fun audioFormat(): String? = if (isAvailable) runCatching { nativeAudioFormat() }.getOrNull() else null override fun pollEvent(): String? = if (isAvailable) runCatching { nativePollEvent() }.getOrNull() else null @@ -80,6 +98,7 @@ object NativeBridge : SpotifyNative { private external fun nativeStop(): Boolean private external fun nativeSeek(positionMs: Long): Boolean private external fun nativeReadAudio(timeoutMs: Long): ByteArray? + private external fun nativeDrainAudio() private external fun nativeAudioFormat(): String? private external fun nativePollEvent(): String? private external fun nativeCatalogLibrary(filter: String, offset: Long, limit: Long): String? diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt index b435fe27..554f245e 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -19,6 +19,7 @@ interface SpotifyNative { fun stop(): Boolean fun seek(positionMs: Long): Boolean fun readAudio(timeoutMs: Long): ByteArray? + fun drainAudio() fun audioFormat(): String? fun pollEvent(): String? diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt index 8d0b7ffa..766df448 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/NativeSpotifyGateway.kt @@ -1,5 +1,6 @@ package hrln.interactive.sidekit.apps.spotify.data +import hrln.interactive.sidekit.apps.spotify.SideplayDebug import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -38,7 +39,12 @@ class NativeSpotifyGateway( override suspend fun load(uri: String, positionMs: Long, autoplay: Boolean) = onIo { requireNative() - if (!native.load(uri, positionMs, autoplay)) fail(SpotifyFailure.TrackUnavailable, "Track unavailable") + SideplayDebug.log("gateway.load begin uri=$uri positionMs=$positionMs autoplay=$autoplay") + val started = System.nanoTime() + val ok = native.load(uri, positionMs, autoplay) + val elapsedMs = (System.nanoTime() - started) / 1_000_000 + SideplayDebug.log("gateway.load native returned ok=$ok elapsedMs=$elapsedMs") + if (!ok) fail(SpotifyFailure.TrackUnavailable, "Track unavailable") } override suspend fun play() = onIo { @@ -78,14 +84,19 @@ class NativeSpotifyGateway( */ suspend fun ensureConnected(): Boolean { val token = accessToken() ?: fail(SpotifyFailure.AuthenticationExpired, "Sign in again") + SideplayDebug.log("ensureConnected connected=$connected sessionConnected=$sessionConnected tokenLength=${token.length}") if (!connected) { + SideplayDebug.log("ensureConnected: no player, connecting") connect(token) return true } - if (withContext(Dispatchers.IO) { native.setAccessToken(token) }) { + val tokenAccepted = withContext(Dispatchers.IO) { native.setAccessToken(token) } + SideplayDebug.log("ensureConnected: setAccessToken accepted=$tokenAccepted") + if (tokenAccepted) { sessionToken = token return false } + SideplayDebug.log("ensureConnected: session rejected token, rebuilding") connected = false sessionConnected = false sessionToken = null @@ -111,19 +122,24 @@ class NativeSpotifyGateway( private fun connectSession(token: String) { if (sessionConnected) return + SideplayDebug.log("connectSession begin") if (!native.connect(token)) fail(SpotifyFailure.AuthenticationExpired, "Could not connect") + SideplayDebug.log("connectSession connected") sessionConnected = true sessionToken = token } private fun createPlayer() { val current = settings() + SideplayDebug.log("createPlayer bitrateKbps=${current.bitrateKbps}") if (!native.createPlayer(current.bitrateKbps)) { + SideplayDebug.log("createPlayer failed") native.disconnect() sessionConnected = false sessionToken = null fail(SpotifyFailure.Unknown, "Could not create the player") } + SideplayDebug.log("createPlayer created") connected = true } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 4b3e49a2..c6388400 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -2,6 +2,7 @@ package hrln.interactive.sidekit.apps.spotify.data import android.content.Context import hrln.interactive.sidekit.apps.spotify.AudioQuality +import hrln.interactive.sidekit.apps.spotify.SideplayDebug import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem import hrln.interactive.sidekit.apps.spotify.SpotifyTrack import hrln.interactive.sidekit.apps.spotify.auth.EncryptedSpotifyCredentialStore @@ -120,21 +121,32 @@ class SpotifySession( suspend fun ensurePlaybackConnected(): Boolean = nativeGateway.ensureConnected() /** Bounded so a stalled access-point handshake cannot leave the play button stuck forever. */ - private suspend fun connectForPlayback(): Boolean = withTimeoutOrNull(CONNECT_TIMEOUT_MS) { nativeGateway.ensureConnected() } - ?: throw SpotifyGatewayException(SpotifyFailure.NotConnected, "Playback connection timed out") + private suspend fun connectForPlayback(): Boolean { + val rebuilt = withTimeoutOrNull(CONNECT_TIMEOUT_MS) { nativeGateway.ensureConnected() } + if (rebuilt == null) { + SideplayDebug.log("connectForPlayback timed out after ${CONNECT_TIMEOUT_MS}ms") + throw SpotifyGatewayException(SpotifyFailure.NotConnected, "Playback connection timed out") + } + SideplayDebug.log("connectForPlayback rebuilt=$rebuilt") + return rebuilt + } suspend fun play(uri: String, positionMs: Long = 0L) { + SideplayDebug.log("session.play begin uri=$uri positionMs=$positionMs") connectForPlayback() + SideplayDebug.log("session.play connected; starting playback") withContext(Dispatchers.Main.immediate) { playback.start(uri, positionMs) } + SideplayDebug.log("session.play returned") } - suspend fun resume() { + suspend fun resume(): Boolean { val rebuilt = connectForPlayback() // A rebuilt session starts from an empty native player. Reload the current item instead of // asking the player to resume a track it no longer has. val uri = playback.current?.uri - if (rebuilt && uri != null) { + return if (rebuilt && uri != null) { withContext(Dispatchers.Main.immediate) { playback.start(uri, playback.position) } + true } else { withContext(Dispatchers.Main.immediate) { playback.resume() } } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt index f9dc7287..01138488 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyAudioSink.kt @@ -3,18 +3,31 @@ package hrln.interactive.sidekit.apps.spotify.playback import android.media.AudioAttributes import android.media.AudioFormat import android.media.AudioTrack +import hrln.interactive.sidekit.apps.spotify.SideplayDebug import hrln.interactive.sidekit.apps.spotify.bridge.NativeBridge import hrln.interactive.sidekit.apps.spotify.bridge.SpotifyNative import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.isActive import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import org.json.JSONObject /** - * Drains PCM from librespot into a streaming [AudioTrack]. The native sink blocks when this - * consumer stops, so the decode thread cannot run away from the device. + * Drains PCM from librespot into a streaming [AudioTrack]. + * + * The native sink uses a bounded channel and blocks when this consumer stops. That backpressure + * is what keeps librespot's decode position tied to the speaker instead of racing through the + * track. The important consequence is that the player thread can only process pause/play/load + * commands while this drain is running, so pause/stop must keep draining until the player has + * acknowledged the command. + * + * Pause keeps the [AudioTrack] alive and captures the PCM still queued in the native channel. + * On resume the track's own buffer plays first, then the captured PCM, then new PCM from the + * player. Nothing is discarded, so the audio continues exactly where it stopped. */ class SpotifyAudioSink( private val native: SpotifyNative = NativeBridge, @@ -24,36 +37,181 @@ class SpotifyAudioSink( private var track: AudioTrack? = null private var drain: Job? = null + /** + * Serializes track lifecycle operations. The drain job writes to the AudioTrack on its own + * coroutine; releasing or flushing the track while that write is in flight crashes inside + * libaudioclient. Every public operation takes this lock and stops the drain first. + */ + private val lock = Mutex() + + /** PCM read from the native channel after the AudioTrack was paused. */ + private val pendingPcm = ArrayDeque() + + /** Position of the first frame written to the current AudioTrack. */ + private var startPositionMs = 0L + private var startHeadFrames = 0L + private var lastHeadFrames = 0L + private var headWrapOffset = 0L + val isRunning: Boolean get() = drain?.isActive == true - fun start() { - if (drain?.isActive == true) return + /** Starts a new track. Discards any audio left over from the previous one. */ + suspend fun start(positionMs: Long = 0L) = lock.withLock { + startLocked(positionMs) + } + + private suspend fun startLocked(positionMs: Long) { + stopDrain() + pendingPcm.clear() + releaseTrack() + // Unblock a player thread that may still be waiting in PcmSink::write for the previous + // track. The load command cannot be processed until that thread is free. + native.drainAudio() + val format = readFormat() - val output = createTrack(format) ?: return + SideplayDebug.log("audio.start format=${format.sampleRate}Hz channels=${format.channels}") + val output = createTrack(format) + if (output == null) { + SideplayDebug.log("audio.start aborted: AudioTrack creation failed") + return + } track = output + startPositionMs = positionMs + startHeadFrames = 0L + lastHeadFrames = 0L + headWrapOffset = 0L output.play() + SideplayDebug.log("audio.start AudioTrack playing positionMs=$positionMs") + startDrain(output) + } + + /** + * Resumes the paused track, replaying the PCM captured at pause before new PCM. + * + * @return true when the existing AudioTrack (and therefore its buffered audio) was reused. + * false means the buffer was lost and the player must be realigned with [positionMs]. + */ + suspend fun resume(positionMs: Long): Boolean = lock.withLock { + val output = track + if (output == null || output.playState != AudioTrack.PLAYSTATE_PAUSED) { + startLocked(positionMs) + return@withLock false + } + SideplayDebug.log("audio.resume pending=${pendingPcm.size}") + output.play() + startDrain(output) + true + } + + /** + * Pauses the track without releasing it. The AudioTrack buffer and the PCM still queued in + * the native channel are both preserved, so resume is sample-continuous. + */ + suspend fun pause() = lock.withLock { + stopDrain() + capturePendingPcm() + runCatching { track?.pause() } + SideplayDebug.log("audio.pause pending=${pendingPcm.size}") + } + + /** Stops and releases the track. Used when changing tracks or stopping playback. */ + suspend fun stop() = lock.withLock { + stopDrain() + pendingPcm.clear() + releaseTrack() + native.drainAudio() + } + + /** + * The actual audio position, taken from the AudioTrack playback head. This is the hardware + * clock, so the UI can follow it without guessing from librespot's event cadence. + */ + fun currentPositionMs(): Long? { + val output = track ?: return null + val head = runCatching { output.playbackHeadPosition.toLong() }.getOrNull() ?: return null + if (head < lastHeadFrames) headWrapOffset += 1L shl 32 + lastHeadFrames = head + val frames = head + headWrapOffset - startHeadFrames + return startPositionMs + frames * 1_000L / output.sampleRate + } + + /** Re-bases the playback-head clock after a seek without recreating the AudioTrack. */ + suspend fun seek(positionMs: Long) = lock.withLock { + val output = track ?: return@withLock + val wasPlaying = output.playState == AudioTrack.PLAYSTATE_PLAYING + stopDrain() + pendingPcm.clear() + // Discard PCM decoded for the old position and unblock the player so it can process the + // seek command. + native.drainAudio() + runCatching { output.pause() } + runCatching { output.flush() } + val head = runCatching { output.playbackHeadPosition.toLong() }.getOrDefault(0L) + startPositionMs = positionMs + startHeadFrames = head + lastHeadFrames = head + headWrapOffset = 0L + if (wasPlaying) { + output.play() + startDrain(output) + } + } + + private suspend fun startDrain(output: AudioTrack) { + stopDrain() drain = scope.launch(Dispatchers.IO) { + var reads = 0 + var bytes = 0L try { + // Replay PCM captured at pause before reading anything new from the player. + while (pendingPcm.isNotEmpty()) { + val pcm = pendingPcm.removeFirst() + writeBlocking(output, pcm) + reads++ + bytes += pcm.size + } while (isActive) { val pcm = native.readAudio(READ_TIMEOUT_MS) ?: continue if (pcm.isEmpty()) continue - val written = output.write(pcm, 0, pcm.size, AudioTrack.WRITE_BLOCKING) - if (written < 0) throw IllegalStateException("AudioTrack write failed: $written") + if (!isActive) { + // Cancellation raced with the read. Keep this buffer for the next resume + // instead of losing it. + pendingPcm.addFirst(pcm) + break + } + if (reads == 0) SideplayDebug.log("audio first PCM read bytes=${pcm.size}") + writeBlocking(output, pcm) + reads++ + bytes += pcm.size + if (reads % 100 == 0) SideplayDebug.log("audio drained reads=$reads bytes=$bytes") } } catch (t: Throwable) { + SideplayDebug.log("audio drain failed reads=$reads bytes=$bytes", t) if (isActive) onFailure(t) } finally { - runCatching { output.pause() } - runCatching { output.flush() } - runCatching { output.release() } - if (track === output) track = null + SideplayDebug.log("audio drain ended reads=$reads bytes=$bytes") } } } - fun pause() { - drain?.cancel() + private fun writeBlocking(output: AudioTrack, pcm: ByteArray) { + val written = output.write(pcm, 0, pcm.size, AudioTrack.WRITE_BLOCKING) + if (written < 0) throw IllegalStateException("AudioTrack write failed: $written") + } + + private suspend fun stopDrain() { + drain?.cancelAndJoin() drain = null + } + + private fun capturePendingPcm() { + while (true) { + val pcm = native.readAudio(0) ?: break + if (pcm.isNotEmpty()) pendingPcm.addLast(pcm) + } + } + + private fun releaseTrack() { track?.let { runCatching { it.pause() } runCatching { it.flush() } diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt index 792c3be5..8d16ef76 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlayback.kt @@ -16,10 +16,12 @@ import hrln.interactive.sidekit.apps.media.MediaSessionOwner import hrln.interactive.sidekit.apps.media.MediaSessionRegistry import hrln.interactive.sidekit.apps.media.MediaSessions import hrln.interactive.sidekit.apps.media.RemoteArtwork +import hrln.interactive.sidekit.apps.spotify.SideplayDebug import hrln.interactive.sidekit.apps.spotify.SpotifyMediaItem import hrln.interactive.sidekit.apps.spotify.data.SpotifyGateway import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.launch /** @@ -80,6 +82,22 @@ class SpotifyPlayback( private var lastSessionUpdate = 0L private var noisyRegistered = false + /** + * Invalidates a pending command that is still waiting for the player to acknowledge it. A + * newer play/pause/start/stop bumps this so an older coroutine cannot apply its result. + */ + private var pauseGeneration = 0 + + /** + * The last play/pause intent. librespot events can arrive out of order after rapid taps; + * this lets the UI ignore an event that belongs to a superseded command. + */ + @Volatile + private var lastCommandWasPause = false + + @Volatile + private var lastCommandAt = 0L + private val noisy = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) = onAudioBecomingNoisy() } @@ -131,22 +149,53 @@ class SpotifyPlayback( publish() } + /** Keeps the playback owner's position in step with the UI clock without publishing. */ + fun updatePosition(position: Long) { + positionMs = position + } + + /** + * Whether a native playing/paused event still matches the user's latest intent. Events that + * arrive within the command window but contradict the last command are stale. After the + * window, events from other clients are accepted again. + */ + fun shouldApplyEvent(isPlaying: Boolean): Boolean { + val age = SystemClock.elapsedRealtime() - lastCommandAt + if (age > COMMAND_EVENT_WINDOW_MS) return true + return if (isPlaying) !lastCommandWasPause else lastCommandWasPause + } + suspend fun start(uri: String, positionMs: Long) { + SideplayDebug.log("start uri=$uri positionMs=$positionMs audio=${audio != null}") + val generation = ++pauseGeneration + lastCommandWasPause = false + lastCommandAt = SystemClock.elapsedRealtime() register() - if (focus?.request() == false) return + if (focus?.request() == false) { + SideplayDebug.log("start aborted: audio focus denied") + return + } + if (generation != pauseGeneration) return registry.activate(this) playing = true this.positionMs = positionMs - // Release the previous AudioTrack before loading the next item. Reusing a live sink can - // leave the old track's buffered PCM playing underneath the new one. - audio?.pause() + // Release the previous AudioTrack and drain the native channel before loading the next + // item. Draining also unblocks a player thread that may still be waiting in + // PcmSink::write, so the load command can be processed. + audio?.stop() + if (generation != pauseGeneration) return publish() try { + SideplayDebug.log("start calling gateway.load") gateway.load(uri, positionMs, autoplay = true) - audio?.start() + if (generation != pauseGeneration) return + SideplayDebug.log("start gateway.load returned; starting audio sink") + audio?.start(positionMs) + SideplayDebug.log("start audio sink started") } catch (t: Throwable) { + SideplayDebug.log("start failed", t) playing = false - audio?.pause() + audio?.stop() focus?.abandon() registry.deactivate(this) publish() @@ -154,14 +203,34 @@ class SpotifyPlayback( } } - suspend fun resume() { + suspend fun resume(): Boolean { + SideplayDebug.log("resume audio=${audio != null}") + val generation = ++pauseGeneration + lastCommandWasPause = false + lastCommandAt = SystemClock.elapsedRealtime() register() - if (focus?.request() == false) return + if (focus?.request() == false) { + SideplayDebug.log("resume aborted: audio focus denied") + return false + } + if (generation != pauseGeneration) return false registry.activate(this) playing = true - audio?.start() + val reused = audio?.resume(positionMs) ?: false + if (generation != pauseGeneration) return false publish() + if (!reused) { + // The AudioTrack buffer was lost (for example after a long pause). Realign the + // player with the UI position before playing, otherwise it resumes from its decoder + // position and skips the buffered audio. + SideplayDebug.log("resume audio buffer lost; seeking to $positionMs") + runCatching { gateway.seek(positionMs) } + } + if (generation != pauseGeneration) return false + SideplayDebug.log("resume calling gateway.play") gateway.play() + SideplayDebug.log("resume gateway.play returned") + return true } fun togglePlayPause() { @@ -175,18 +244,33 @@ class SpotifyPlayback( * the call or notification ends. An explicit pause gives it back. */ private fun pauseInternal(abandonFocus: Boolean) { + SideplayDebug.log("pauseInternal abandonFocus=$abandonFocus") + // The UI clock owns the position; the decoder's `paused` event is ahead by the PCM + // buffer and must not be used as the resume point. playing = false - audio?.pause() + lastCommandWasPause = true + lastCommandAt = SystemClock.elapsedRealtime() if (abandonFocus) focus?.abandon() registry.deactivate(this) publish() - scope.launch { runCatching { gateway.pause() } } + val generation = ++pauseGeneration + scope.launch { + // Queue the native pause while the AudioTrack drain is still running. The drain is + // the real-time clock and also unblocks a player thread waiting in PcmSink::write, + // so the player can process the pause command. Stopping the drain first would wedge + // that thread and leave the UI showing a stale playing state. + if (generation != pauseGeneration) return@launch + runCatching { gateway.pause() } + delay(PAUSE_DRAIN_GRACE_MS) + if (generation == pauseGeneration && !playing) audio?.pause() + } } suspend fun seek(positionMs: Long) { this.positionMs = positionMs publish() gateway.seek(positionMs) + audio?.seek(positionMs) } fun requestSeek(positionMs: Long) { @@ -200,10 +284,17 @@ class SpotifyPlayback( suspend fun stop() { playing = false - audio?.pause() + lastCommandWasPause = true + lastCommandAt = SystemClock.elapsedRealtime() + val generation = ++pauseGeneration focus?.abandon() registry.deactivate(this) + // Same ordering as pause: let the player process stop while the drain is still alive. + if (generation != pauseGeneration) return runCatching { gateway.stop() } + delay(PAUSE_DRAIN_GRACE_MS) + if (generation != pauseGeneration) return + audio?.stop() mediaSession?.let { it.setPlaybackState( SessionState.Builder() @@ -226,6 +317,7 @@ class SpotifyPlayback( } private fun publish() { + SideplayDebug.log("publish playing=$playing positionMs=$positionMs current=${current?.uri}") session()?.let { session -> val now = SystemClock.elapsedRealtime() if (current != null) updateMetadata(session, now) @@ -318,5 +410,15 @@ class SpotifyPlayback( private companion object { const val ART_PX = 256 + + /** + * How long the AudioTrack drain keeps running after a pause/stop is queued. One PCM + * buffer is ~23 ms, so this is enough for a player thread blocked in PcmSink::write to + * wake, process the command, and stop producing audio. + */ + const val PAUSE_DRAIN_GRACE_MS = 60L + + /** How long after a command a contradicting native event is treated as stale. */ + const val COMMAND_EVENT_WINDOW_MS = 2_000L } } diff --git a/native/sidekit-spotify/src/audio.rs b/native/sidekit-spotify/src/audio.rs index b8bedf76..88fb9805 100644 --- a/native/sidekit-spotify/src/audio.rs +++ b/native/sidekit-spotify/src/audio.rs @@ -1,7 +1,10 @@ //! PCM sink. Decoded S16LE stereo is queued for the Kotlin AudioTrack drainer. //! -//! The bounded channel is deliberate: when Kotlin stops draining, the librespot decode thread -//! blocks on `send` instead of racing ahead and allocating without limit. +//! The bounded channel is deliberate: the consumer (AudioTrack) is the real-time clock. When +//! Kotlin stops draining, `send` blocks the librespot player thread, which is what keeps the +//! decode position tied to playback instead of racing through the track. Kotlin must keep +//! draining until the player has processed a pause/stop command; otherwise the player thread +//! cannot process that command. use librespot_playback::audio_backend::{Open, Sink, SinkResult}; use librespot_playback::config::AudioFormat; @@ -14,11 +17,12 @@ pub const SAMPLE_RATE: u32 = 44_100; pub struct PcmSink { tx: SyncSender>, + writes: u64, } impl PcmSink { pub fn new(tx: SyncSender>) -> Self { - Self { tx } + Self { tx, writes: 0 } } } @@ -41,8 +45,23 @@ impl Sink for PcmSink { } AudioPacket::Raw(bytes) => bytes, }; - // A disconnected receiver means playback is shutting down; dropping is correct. - let _ = self.tx.send(pcm); + let bytes = pcm.len(); + if self.writes == 0 { + log::info!("pcm first write bytes={bytes}"); + } + // Blocking send is the pacing mechanism: the Kotlin drainer writes to AudioTrack with + // WRITE_BLOCKING, so the hardware clock controls how fast this channel is consumed. + // The player thread is blocked here only while the consumer is behind; it wakes as soon + // as one buffer is drained, which is also when it can process a queued pause/play/load. + match self.tx.send(pcm) { + Ok(()) => { + self.writes += 1; + if self.writes % 100 == 0 { + log::info!("pcm writes={} last_bytes={bytes}", self.writes); + } + } + Err(_) => log::warn!("pcm send failed: receiver disconnected"), + } Ok(()) } } diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index ab126c83..05f773ba 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -248,12 +248,15 @@ pub fn create_player(bitrate_kbps: u16) -> Result<(), String> { }); guard.events = Some(player.get_player_event_channel()); guard.player = Some(player); + log::info!("engine.create_player created bitrate={bitrate_kbps}"); Ok(()) } pub fn load(uri: &str, position_ms: u32, autoplay: bool) -> Result<(), String> { + log::info!("engine.load begin uri={uri} position_ms={position_ms} autoplay={autoplay}"); let uri = SpotifyUri::from_uri(uri).map_err(|e| format!("INVALID_URI: {e}"))?; if !uri.is_playable() { + log::warn!("engine.load rejected: not playable"); return Err("NOT_PLAYABLE".to_string()); } let player = { @@ -264,15 +267,18 @@ pub fn load(uri: &str, position_ms: u32, autoplay: bool) -> Result<(), String> { .ok_or_else(|| "PLAYER_NOT_CREATED".to_string())? }; player.load(uri, autoplay, position_ms); + log::info!("engine.load command queued"); Ok(()) } pub fn play() -> Result<(), String> { + log::info!("engine.play"); player()?.play(); Ok(()) } pub fn pause() -> Result<(), String> { + log::info!("engine.pause"); player()?.pause(); Ok(()) } @@ -291,6 +297,22 @@ pub fn read_audio(timeout: Duration) -> Option> { audio_rx().lock().ok()?.recv_timeout(timeout).ok() } +/// Discards queued PCM. Called when Kotlin stops draining (pause) and before starting a new +/// track, so stale audio from the previous position is not played. `PcmSink::write` no longer +/// blocks indefinitely, so this is a cleanup/safety net rather than the only way to unblock the +/// player thread. +pub fn drain_audio() { + if let Ok(rx) = audio_rx().lock() { + let mut drained = 0usize; + while rx.try_recv().is_ok() { + drained += 1; + } + if drained > 0 { + log::info!("audio drained {drained} queued buffers"); + } + } +} + pub fn audio_format() -> String { format!("{{\"format\":\"s16le\",\"sampleRate\":{SAMPLE_RATE},\"channels\":{CHANNELS}}}") } diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index 5bc43e22..37ba640e 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -172,6 +172,14 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB } } +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeDrainAudio( + _env: JNIEnv, + _this: JObject, +) { + engine::drain_audio(); +} + #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeAudioFormat( env: JNIEnv,

@2(yE{=Vf{>X%b z!|oZRQ|EK?os+Y)Br!+5MC8J-j_*-rTSahW%vQ|flO^CY?sL0Z_XKj~-TIRcNMo9%| z>zXBQzcs^liS;mx07@bnd)M@QmPw{UvuGJ4L)b#~MfwU&G$1O1?;e=O_T;>}>J2Hb z=vQig#AKIn?*f1*pYPM(9ht#e_l8_<6t?JA7X9tmjEW|p77|9MSWOuunb}Pu#NZ1X zPCbWuH**WngBA*1dGiBirl8kk8)cpRsRhX6LpaMNU}qMl3|Hq|3ROrbC+W3x9z+#@ z1iZYh1=`r6G2;k6vcdcLI}ihxuDfnXZ8tF?$#-*|h8UQ` z&ElvATS&-}@-km)H;G!Q{5H8PDB2sp-L23X)7~z87wb9}di+Fy{sCHtD7mRGI z9l>d7XbpS&t;ZmPuvy7weDQ3h(FL@uaOfPV|#*!1hInCpq> zCxHhs*CLR%|GJ=r(I`AD>{9+##$-QH!Fh3T?OV;ocb{D$*a=xA2WVe6uS4?96Y>() z2N!8+d9-GqegPNq-vloNE{rR4h2Rpy!onUW_4HgJ(7MMFzOa^4VE1AMD{Rg|y#I!h z|8dh)RR-(kz?Jq7>3X zF0=8vA`cr1J$hxHMx}K-pl{vBYgrF7n!5Ly4R2|Vpc?u>a)mq7*ZIYnE(dwd-W}Vj zargGAP_~Q%1TPq7SD66hc9y`7s$;R=s!ja5@4RHaKgJcU0$*&^eG2JZxOB!5dTw%4 zC=$Y&tw=RlzdOo%u&Cyra@-{a2E3iWZY2(G{`&xQ*A>F%+{WHc$kn$a4(FjK@MD~c zgZH!bAdrLl@eB8eKZC?@pV^pEhr}SUJy7JHSn~!-xaUUQTCI(Ftbh0ign$LlA@0{9gNf1I9kBN4gj9FsQa%NIxnGx$e=Vt>RUa}_@>=nD*B{rOqk zX(#+a+_voZ0K^T3>(~;;!`I}jZK&j&CBkSfPJ#r~WGz<@UMpA*hhECPdol2SRq ze-`}%qJ7P+gC2jG8jv{ppo4Mh9w*9))Bf$8$zS+p{kK1}arz%kv!P9z`~eVM&&KZ7 z4Lz^0^O|nJuR83>@G=_gt_gUqnT{1 z=q>IHPh$z`y~r9f_niT4ZbEoj2FzoYzX$WITaV#H8jBJ}o0Tdb&j~c_|8d3CR55l# zi_Yq?H64wupHy!hTt$yS=1A>Hh+HOLlkc`jZ}-a9JEc+K5nfxWU*tQ;8bOc*57Fq` z`1MRJi+enq2<4IsJ!n_+M*j>UkgsRQjj1=w{a;$D)7@Et%*h>$^uQ{$uU}+31_^Wy zZfagkek@6>$Qt6paSXBsH124i7`Bt(_G8#jD1^Y;;G1!G`H6ftzLV#Cv_a3_TdhFH z*;S2KYCzaLemk{yiDA-olWMj)c%K+fM!hgN3vvJ~+(a;%V5On~&2)m)rAV@kS`rHP72Vp@lu*9`xYdmkRGbd4{Yp zxx!dKSO-r|!g(X&vExjPP?kYbyJ*Pr9}ED=Cgyc5Pj3eSn$?4|rk#J5qrrcUuVbes5J|96!;iFILF!;3z&%sWSH1TGu+!XUVyGi0O zOp>8iUnPB%?1)dJRIM=s>CySK=b$QKW+RQC)nSMkCO61Ba79+%N}?=A{Xmq}A@ZCK zofN!C$Rv0;XFowICuft+an3tRFy@AIZuIq~Z+R+-(>3Gt?6Xr-fXn1;5F z5TD-oBDKrO*0$vFRPdu=nMTQ#lIHAR%rof;5tW6O%pGKcP4TGDF)}Gv9ro)($57I; z(5M+olK0rNeD?Y0WQ(mKXxYrD>8d1;?-sil{}W}!nwtUe!BDT82*(cz_08ucO|z}& z7-(Eov5CdLoChFpAAL5D@v3q=`^w+9HMpney(*bt48zF%{r%0!2#R%;xpZb(yy_Q& zr;ytQ2u~$?g7-;>V?JM^J8wqv)@|go4%N|v5bUhi*x1BiYJ)0rr1>}U{ikXFCJOE^ zETsXIai8LuEz`_N29{vHr{kXpX_(LO@kVRtueLH-``UhZI~J2%Ef_GS&KdA^_uKth zq}kd}4fLg=;+p}TFT?bme$v-3HoFVkQI0K8IeP-Hm#Moaz5$kY4J7-Hn$8Z~qhHwU z&FHpdthYST{Z1RE44<_c6vE3=igrSck4ZgY&35vwU>bJ_@6^VXq#6ZQ#<}C4msn2n zs!!}mH0S=+t+^oPB->`6AMExl!|IiZ$fKc~B=7TH;nIA{VGUAJ0~eCsL=eBHG=U;p zG3hFp;vIjw=5vz@tX?%{Y);qwLcp|vQV zp}L7gxLAlr47C}_(sj0j$LlVPTS3@MAK3$`J;vR%&3$8Etr;r!ZLWvj@?Ja9z^i6X zP5o@UNpfXsuia^Py1CmTcZw(PFuCCA1j+d)<-hv(4~&~M@}Z%@%eSz49|~Wq^vIX8 zVYq53vd>8rXcgfwI`kqvRJfgJlFZCIpfspkpUB zPAo3q`?a3e<-|9E;8bq~P`u?Lp$_#V>k(l1+b&gk&=nbbO*8$>!(TbxSQTRmal}ba zV=z&=BPs)ogTWSJap0V zwy#%0r<$bw-*%eg>KCRx6F%C}omR+~|}aF62yqvA=1c^JbgqLiDn>3TvKAjEFDk z*AD5)jf@T(18fRvpCfP>DH;P^pQI+|zmhAx*(o@Ex$)KYduw|ytauO*mZ28zY5N2s z<_VIbi!ifwA~g-je6YYbKmD_YgzyQJe@cU>Og zei6G5awv>Gk$Vn!YW5Thug3KsQU%ys6*t%*VU-;`?64 z+}^eER709`G^zdZ)bc&4EmX!Qqf*DO3hyJ$4h#lbI0NpLjx`3;eadzg5eUZ~Ny`T_a6>_5^Bh~rJWRv$wwFs>W_D!dUUAnn z+iv~}j$&`Y=@#?o`ANqEDTkF-hSG}y4EJ8TjyATBsqqqe^PoCtS-@NJ=|D!wQSFv1 zdq7R&oQgfwZ<$fEWPKrYq$FVpo}DZS{Q&F1wje+>w9XfJF=m}5+-z$&lsZkvPZ56W z#+vkD(y$DqF)@Fg7YK!`Ri|i{8h;LOg;apUU$RORzygE;k+9JrH62ymwP6YRXTiXuB!Ch9(!reF%zSvv37_ab5KA zTil0Nm^P}4PqPgLhN|?MyD=785fi&XqA~va2yv2htrDurC_kEH`Xv6b^G3DgRD0pm z58dkgnKW1%*bH6nFJjcVXQLzn9;`+SjI>OI{E%O8wGA3@0~tq)OhC=Kb81^&+%;uQ z@j~2=P}U$NoiZ#*Pt?UrQ}y)-h@ja-a_4p7=To8-Mvi4P7+NN#1;ppvl-jpKIE6{a zKMM?vmdpDtag;u|4kn!*UW3iIuPfg?+HcbqAI1el1Tm14dJWjin&mm2HA?Ve6ZG#% z2%&Erp9Y!I3W<0@2rpLaGl%PMJCZD6Lq01@dgV?lC8Z!r^hH}=bLc3H=)$KP&3=kX zMz*dx(hhe^!e3Ib6)iN&C=2DmW@} z{CuFli;La7^-ZstWUk^A{;RUs0wh~Ol-1v4v_$Iz0N(qPf%S`DDYmAwQk=l+>4ow0 zz%w)iirK#0P?n_5GqS4{48WU+n+=&=YHTJXcPk`1TnZzQq^zda!lecYjoPS3k(X0m zq7F8NV8C_V3^232)HtwF$yi7JvTE~VBtye9`|BT25u^*weN7%0zG(|x0i%AYQAp(6 zq%|urn2F5}AK^Gxn(@pR1U~q)9RyOT@D{=75N}awO*D-0Dj_jcBHsXIW4LbkWBPNG zTb{ge->h>k+3w|MjosXtaPH)*SI?StF;ILjh7X#0lD{al=V`XxrUX_@7KtaJ3|H{@ z%ega-rJ-yval4xeygMbFo{6j#rbkclw%@HSeSb>kQJ5ZMVsbX$8%9TRm-Pb^UW6D_ zSUxqE=g_`FQcS0-HsePZF*ALc`@DE(zh`n{G@#pP; zug=*d7Y*n}zR5UdAy_)#ynPu2mx{h3$2>~MU34jH}C5B`z1=KGxv!Spbj}o3<=p95Lp&y(1x;R7RmW% z_X6fo@Oa%)0&96BdWcxbafZkiegq)m*ao1o_8HTw--vax2=btl6i_7Q;_l1^Ox>}W zT9}Fb-^7Nck+-ly!(0};Up8G?@4SibHdE=^cR?Lk${`ehXCT{TT@DwfSR4rmpKq3apzk@F}+a!c~ z=8aeiNd-O0KOKf&rj1pUZz(KkItM?>&;JG?qq*(?um(H|$eb31%A&-G4}aTy8i2I? zu5l4&;^*M#H#1G`smp#rW2Amj%dH|T%#}rV@Jo3V2MJgkYM$j?qMa*|0JkvoPUdy_ zG)y!SUlH&b>5P+)xNPRG1c*;stiIhh_vovZkknvG26J~+@EvnkCKMWP5-6AeA#+<` zva5J%t^mfbh3M+tTwJ{ufPNulFA#{~^=k0mH*FD{Ern3y=PkV?!zj40LcK+JSX z2l+*{)?Aq(W~M~8T7Wp3rn?O)RuYON-@(LL(do+0sWUBOEr%bmHnJ~G)EpGXfjO%P zR%X`=@$#>VEIl`Qp$lozod!3^DkyDXYzHu|zl#5y{$FBEL63lbxt8eygDG)5C5-{~Wi zzJckpG^K_Mw^cDG-YtWe&wmx#AXfmxcctyaQ?kuolJ~bDns)#vtp1?zXpI4fV66Cf zMsUw#L=ia+?0Gui(aUCpnp6TLTkPWaG(ByslY&;Pu1qW0s}DrVSchdL4bU`iThRk( zlSm09yF>SsjOGI}y2~kjN=Eb25iCc-a@5Z3Gem2k9N41@r zd<$VEa;r!z0xs&E^#OeFF#Sk?qGvWDuL=vDt}&L_aGBF(=RF5F?hLX`E;HWj`FW$m z=51aE(82Ddgg3n8B@^qQ_XN_3Ez9g6DmcynMwtC6(=rpl(Q`&Adbq^5z_fRw@7+2h zsPAwShxbg$3n+lmGx?*8wSZ8bmqQBuC6qVd5R`fGKO-0{lGsgnhiP>)p20P>faUuqV znZX5orzjU)kA`C6jh{boIr{2Vjo%#;p3agvQ}Vcq@N!_MCeWw&ehV2ad}cC`rdv6X zpeZ>3VxHdABu7wNbQ4B{BG5FD@%7sgNGfgPbB7o0yLy;It+Lh1G4pJS!m0`HXG)#F z>2}dT*7@JBBm{BmLE9~24R?fPdH}Q?N}Vrs@Uti>7YtCLJ6gSet1>*3Rwfspt-)UfiHK36Hi4W1d z`i(LXa20MZkmaTMuG$zrv7xo(QFS{8G*!rK0^nUfFAQ!XnZPyG8usefvvB*_V)Z=Z zmyxVUet+hz%D{-5FLKe=_WmXT6y~!j_c$hPdFeLc{a{2}R z8`KNxcK|ZHYL*TlwUy7Cl3@HikO$2GK>FBy+-(T8on45JW*OrxMwIQ{8~Zbl1qK#l z5GW6mgdxJHLI{>aDoFix<%jI@+_3bxPzvdW&jdDu2Oa$VJR=vaG0cno%{C~u_t+-a z9L%NWh}`JJZpQdaiM9Db(m&64Z2*#j-CZE9VnaoWq5PM==3FkyH-vx+$AHzl z1GB(@N#kY#=nwgT0j-%SPko>i1fB+nJ5>XR1Te+C?oU1wI;kf!ZyeoI^C3md!N7c? zmk^~+I2Tp_d5iL-y-IbOS{SK{B)G4bJ|w@Owe)rA2c4QSfA!>>OIfr@t^TXRwg6=< zR!oJz3o$%OzUDc;Y>I%K^`sQ&XWl-5{3P)QUR(u|xUtj_zmI3`ov10M#tCuT zDYd!VHp(}Hw1Gii`zfTHCg{R2S0%~I+@P3veV^5DaQyf4k}L0$SnVV#Pxu_Xp46r@ zYb*Wm?Y@)s#R{!SZG^E8*l|P|f|Z}mU|6wVxjKvV)rMWQTlMHGoppa3CAs(|UI-hq zBKE|xTp4O0N7j6rw_M4|gdHaIlXBmAvro`Zo(J5wA?vjievK%U`{4$a(1Yb163yvK zeW~lqt$}_e7tGQlF7qu#ecra6YJ<%smYu!+X1p>qBHR&yF&HcbxJnfsLT3A_Hz03q z705L^Ty2@S{Zm`^+%+#@-mFSeNknA?N0 z5GNC5+56n=Bm+shts4htdzB~y2D=0$axKUf=^I{m^Tv@ov)^_f+$eG9+DZej&-=cY}0Gq6KBT92VV6h`QY%y%eOvEjF0jkCl*OAlFQ*Smw?jY9TaaCO>DMJV@ut$+~ z0?W-I8NU9`idmNAiX;)c7T-(G0IX_I3+5SLM7*T|{U4ynelNQ5Dd@@l7dilSzO1Of zq6NV@8R&GOe#(ol5un*xtx4|HQZr2oqcH`>i~}dHCTmN6$SM{L2+Elt`OTiTN?a9S zTzMFTNk*#iaRknTgzyuwxGV65J4zH%{)!}AP!&BQ<-3_G+L}7!%IzTl^sXIfi> zqHKI+87k--32JP#l{ovMf>Oshy+V!pD>h+2>y1+F`$;@?_5un6>8+JJ5L&Sojds#g z<<-EajLZ(7;Tp4(P91S+I=XC9xGQg3LO^*O0nH%_uI7`it-VhJyBLz5E9()VmiKKX zUQ``!vU1X`ZEih|^LsRuSV>iTVHe+UOFE!gtG9HpAzP8xlCfpP*Xu541`8eL zySyGw-~FB|n>D(Wrh>_iaIi{foVh<%p<2J8PKlqf$#IsJdG51-m1^%`v4Hg55fLnbUT}&u8{2F3&)=O|p9}>l+;va;S8fOc$G%KZ?4IZs(3=fr=l%X*sf4s5 zpyEIws^?N{oU2+d$W(+>`m2RP9>J^F`vNtG-xXRz0x?R+D&3LP^5w`eA_CGfveQ1=o9v^AvsIGAxc8wzmfIRv1nk);FlDtZGS1_~KjaZD|m9U z=q40;Y+S{@cHf&%n}9uw_|pnZ6)bE}Dj)tN`nsQmFO5K6QJy_(M9-|F>P4L|>XOJS zEqO)#k~6LTiEAGukr)Gan5Lw z)WG>cd(6D6msHq%77sQ0fI{Hf2SZb6&lipWo`Z`BsSq;(;|sBypuhuK2Yi9!^gfUh zT|ONFkXBW+&09ngPE>u7KjmT|m*qQc8l>M=vHweb$G;zyPCe;1sLM?_*AY(;IM?j| zi9j2~?XUCXq{Xd(V55E1rZ^qW-2(szda*|aw=x5?P-;bh18tWJO)uuW_cxh9c-4C? zoZkeMTIFtucVZsIXxirK`XVfIPRfWdi8C}z+JG2DY0G>2P%e{%-Z zeIsr~2OF|-%j&V7bm1nNeYTk#5Kf1>@g~f;3FQGlkrm!$EIQPfAxQ<93PUoCYVNyT z{#qyS3Nj{NI#nHl-W{9ELvC^*DW2`rt$Rd2g{Ylo_h*fcCs$~G#PBx1kRPizQPmL1 z`_nOSeC?+E##PUjYD-_FR|G4=en`v=c+OsIN_6WbS`5c}* z!Puv9li0xX5;x0LUga9ir+aiOn{ZoKcFEW{N19-mme#Iwzjzqul(x@fb%!SFD}g@q zwU0Wla&Y{bsiSz(LP|V_^5&7(NU3nL4K;Mg;KCWVCiUOOIzRo6P-YNyWd7lRXm=oX zYw(GtZ>r=Cs`FbLVvqm%cY^Z7PDqNIRH>fV1!wNnLD3GrD?$2mE|omR9lWxt=U4*#JlQ`%ye_R_BAtlHKQlv`6~K4@`Xz8W*-(wBzVzg_&+ zt38f$Q9O_QK!AGbNF?nx-d&7fXoc-EAs7m76R6>^>i1A3^m@qPiEqmmHk!nrCX)O4 zQMHuHE~k`?ZxzgdYDsKjMyzk9P66o>rp$F3)EU(=cmf@PX-ndFijV_8*#ek9^Qi|h zlj*5*Ak7=h%{F0_xLDd#SkYR#fek7!omLCDGo@I&&>`h}EFslC=H5pM7q}Zza5+$; z*zeI|V9!}NfV!&iiM)G!Mok+0O}DI-f_J@6%_(JNT2*p2A!)aIcms_)@Vy|4w?Dgz zm}OK-Mln%dE^coKY8_0D?)m^DSiVYw&$eV}n<-(B42RgiAeJF$kq%nFk}BV=GR!FT z>3t3zwQyH!#|U6SKA-F=k#`PXE#+=|n2u_)qfy(c)W@s-d8Ns$`PR2ZF@}yQ)5*EY z>TBGLuuX|c)~!4VJH|Tc`{Ch_9{j6&CeW9U9bdIc$#{Zgs`cW_+XSm4UpwB>WJe~f z(uXU*2$TM4(F5~e{*0Trdex^f8^fl6+$^PYALx;m3=h_H8yu~=ty+t(ouW!+%HI-B zP`4YIQkFcCwww##!Dx zD5ZjhCS33{;bMECf@I=%w1qx+?}$vlJm3}eGcUJRpwxtk5p)Pu8@rckc{slJ--fFH zTnbJ}v=|z@gPUFuli3Z`-*FLGmovg@((+mb*^VL}sj}zYw;SEKVdxIiNpuqD<200^ z%zt0_wolbtD~{6PLGQbe9sm`pc$O`}g29T8D=00$BFVb&-E7*|GEv`QrH+Ul6&ykH zxH8^EeWx^|7I#syk{yW<>NslYEMW3NQlu7RJOU8w4VZg!T=A!j$C0Urs2OV9d+O$g z*}!PkBj{dh4wRh?GS;yne@mG?(Op5>`4~`CV~(EFjU7!%>nRz#|0%Pp^l{pC&=VWz zB7KWzecz+aBMMC4D|O|1mI^Ks|LF@79DvHE_PZ^$Vo*#|ShgJHC&n68t8;nJskuKJ z85V9;_8+5l6UQ>+x3}PNKDHeQK>@L&vf1qL%iPE?j4jd3cDVHB?;W|Dy|=rAe<|4JMGyHk=@ebt)gX#Nb%P3cjWgk!HC?>BUxv3YZ?yd#}b zl<{>J9@I!^)wgBw;(J-B1dmT!m2AD0*21d$htxqc(W~e6KlA%J(YU5~b-vVX)c17s zDq~MsqvxGYeWol|)yL3b=Wf<=fZ1aJ>)X*SQcDPe?>z0!Qg3T`v7y)phn@CSuF0pUr(`PB7J7K)SRFr*JM{0qN2H&<;I8sMRO79~`(J9vXxXi?M4lS~*AJy7VmA?oD0Z;Zq$Wqts=40sKYK zM|fz&fKV1D8`r-+95Q+wm8|bE;n4K!U@dCLG+_Fwkc7Y<1vt6sNoc>4t5vmlJKI~8F)CF&zG$5ZrZl-)6d}K4L`SKOSO+D*5LvQpett_I}3ggPODVCFm6M;!d2#rZ@Jr&g~v;-cYX7i zKipJ_{$8@1(=IAXKh~NJ#N$bdpg{46Nb1JFp00l(V*wnlUq$WnGY2~TZu#F_^#Q^f z`G4}K>}P*ZV1UYohx4da4kv@Y>r>~0+2qqO-+8-FfjiZ{b}_+f4^#m9{U-?matW4` z!a5l`$vZz~99;AXXHJ~KVKYt;vKbK6^I0d-gtJHS>6u~tw*6evMW+i9uvC&9vG@2eC9l*_6(qM;+^=5N$Ne_GT zaocB!Za6ReK40Z9$Gy4tCjINN>+xo+{zsdMG3kaq*BUGchtL;AfGBuT(qYA={BS9s z8*GO9&frXTHm;}i3+K1-2yIB`4J-&v;HL@*?@y)BQK_IJ6{OMkH`_Bm=1)&_d4YUWbl2gb(Bl+c_$ z8g3gTzhh*FsCyn=Bk7|=x7J;9P@^DPs=8<1iz&C_4LDE43Q(H#9pDuc6jb+*Kt20$pgSQd7r1oxaZ~eL zKGTg-6Iq^EKf(_xpyQy+k~8mQ`&W&Qz)yLk=-RVJ^NJ@`+7kbg>CxNcOn#|hHC~FR zc;fN$>;+NZ%mm-B(j`v5P*@$7YRl*RH~PjEGd;FpK{v-`%ZklvqzWJ&}T5L2F{8R#CF-wLC<`f5p}S$awny;>2~rZ&4&( z&Z32Ot1Dr-@8&h#e7bp$TOLZoDIrM_G^Op~7xvHegVHOgs1sq zHUU-kP%8z?Kz65eCpdGxr1?DjpwUB-BGKxtJ&&g*arIKI2~Kun1Afv#^^M|J_eTAp zlNZ^Jw2vLBqvs~=popiIRFgf-sY87yfkCf{^lL|={vgW{t28-e-NM+szjKOT`=$sK;gpj2(H2x;fdT=p|N>#LBGmeHQFT*a) z!OwcnzzV)N^vuKWbZSHg%~UR zA(qtSi{&ab{Fdh>7CuzOGDMMp6PQwPiPSvHVV`@ zI@|(23-`N|G7R>JzLA}g>nX>V7vcr_8-NaCmR_?qPF6#3>=h=SVO2IVj3E{5b5G&F~){qX{_{R)?OA6SP>;AdViw~Sy-k?Gg zN)@1%e(l(>BPiz|1f5a*&QDe4(w?yw+D~=*tJ_|*_Af~|96P2~b9Ys{;fzmYTNT<5xbUwiMN?z&p2A?r9ylZyxQbk05CdI7F!2yh&F!ZJt$wWQhBb3F{CQQ1-2a zfaNv{vme8$6ySi3c_x^aS<|V~28T$zr4d_qd0y?C=J9;NTUp9z&TBh(?nLz5Ud$vw2L9;9qbh>K7Xwfxik?*lr<>oB77tIAcGmBr;^f=*s0 z=fRg$6=LY_JrIf$@3vza3=kmk-YD9Y2wF{c5I=c5-4$X(J!6UDJrL>OdCLr6GHa)k zPHk_mbb0;nV0l;ucW{-eFtt$p;G$V(rzs?#-DG_^<#oE!0xnNf^Sp@nr^yn={ECgF7)zTvJTY$1_Sq}VmMmsSYY)4 zEJutX2@re!7l9C?e60DcCJZm#JEGAN{jF8~CqX9@aP|Pjk&&_bvi>@lXr4mDfYkNR zZ(&$t{~y}nLUQY-W$G2c;_1<}$Om2O3yee}9B3ZnD*A3J6kbNyz5rU$xC$j~j)z1G z%gH8^HPRRo(ak?ZF#R`xb!ems7)Qz6Q>_!;4+e2;91)fB}5W0!s0BSG) zyb1o4GyVxgd@cY2QwDNRcDObXNW@VE5WL=qkGC8pGF{tJ09>cpF-~-@2Q)G>B{$*zIOk2vtIOoFDdH!~qH- z>ExJ>-D=7+yh?qW{6s!64D~U3YRw`qG6#oet@3rf+#wy{QqkaBJd96fe<<({5}X;( zp}2K|1^kc&PsWQbz2VkF zkI^mvg2*&bLxWCQWBq-c}} zd5Ij56OPJ~`avGIdx~FrG5lsVdXTSps)Gap3a3TNaIGLIhQ_ONlO2pOHa`2<;S1!q z;5JY4*?{tQU6kH{al~(^HfJBo6mhe%!Uw~yDbjw|`|HCJm@>a(q95o|Ut%2`q>4u$ zL2kpkzlnt5k#@Uuoq(Lm`C)VcaQ>L-^uOwCtm$Cs#asm>&!_iRj%Z8sT%ZcjB&kuC zF!al(Jj=V4#d0v^5&L!xkf@P+Bi->}%UBG^dlpT($R}A5+M{Hqugy=QKn7A(UyHwf zCLjfJ?k;5VvK3Sal>uK@YG zU+k%-&kEoFcoVd~pT(QuLB@RS6wm9&iN7ZY?Kvt#QTYBLU@L2{sskfI%qw{Wcw<1@)=oH%GeoS2RXCEguUW7_RMoxd>cH3@FUt!lpMIbcg^j z+c3s0|Ie~p>I-NNcz(nIWntcGAA7}s^36`56Vu&m3j;Jd0e<7+w4v^&K+ms622NRE z(@&=80Ju-7HZ7PE)g>5W+rg>v*B}^YZ(yNt}?1q5eA#= zcnkx$pyV;IxYJ4^l-wOTa__EG!KZb;i_xs!!koIQx0^Psl9+=2s-_dPR8ub^CV+&Q zf24OoWwnwzVckoaibF4UyM#P37h;A8uSvz!pP!wuU@93_XKcTuoq=;fud?n3j5(aX zRXjA*DY`P8m8DV^DKi30qRQQ2{r`NEep9oP{&939eB|3@Eg&1!$^wT z(eEtnt3FVsTHbJy>TpK_2bR>I^sDay8_1&%m!Nx6O-S`nRcO*8eqhxIWE2GAH|M>& zud0eUd%1#x^TF95Lw;791JOcJ!2>;#k*C51qZTf_bfGcY$=l$_-8%dWE2Y@vn%dz( zb9X;lJ*~~LOq&C~5g>)BWjfKAy{wIb1Zl<6Dx!SHgQOjPOXAPwFg3ru`o*_2o|t0- z9m22`Q_x(e`Ve?jMeovep*4Q&xA5`p?+451kxYXJr$6PSJ(U3R4q6=vr^@$usXx6t zyvI!f(F2D89Ta7t8pd9?G|a11UZ-fh;pXe-r- zi(D|UqA?@b2J^C(wA)5m{gATBtr<$CRkQ4R8ic9AhU04GPKL2-FO!z^A*cafoxbQx zk2$H5E%bA@x1ex;22~Gt+OC0uD+j+x=FY8de_ExlEE!KVA6=1NKVIez^xpmz;d~x0 ze|vKtO&>$VO^;y6%VK`|^8*#Cz^K|n(Mr6*_4|bLvkw2CU<`52G8_PqWJu{+(V=$> zTX%uIo_cN1vBRSznf;us^3oP`3*bnOI4pG(J~8;Ej#k|8S!o9v@{#4ACQ~$+dS~Lc zn!s#bi+SIH%PB^bwKwsZ;?+D;dk|YpnzQDTQ}89)e$(j&XUl0-V3V06p@Q(}+U};r z<(UtPDseT5->EukkowV$x)geSo-kkW#50i?w(TAoqwjKquLrU+aF}C``h*uXA4G0; zHk(Ls0JdO2As)jl?j6Qtq$({K9Uqq^q~;a36y^;a3RI4MRRD(G+&gWol&xx8*N)n? zuECc)>F2;+0eC_c))6JA>1K4b@~XZGXl}vC`zx=O_f_>06IX2Qa5iVn#I1c*2t`IX z5OW9rngP{o8~2>7c{}szQ{LaeQTARxPicj+v+FZ!)YM~PGI0%_NW3UsS(x%&7iA6{ zbPI2~lzpl@Jp++UEM<4w6*pt&mBrlYl^b419QNu-_32Hp&5~^M_Ij6BR2_zp=Z#>X zWWiX70+XV-WNDdFXIfOuCe8=KuJg|;glevoWH{I+!q55x9Ug7-Yq`R*ru}f@{mE2m zmDSce3UYE%OzLyq-#aunmz0VkGzhHO3J_$ZOt_b4sJ^+n-22@e*WI-C&PNs_hUC$S1Jmt{fJ)p3d!05YZz@|;z6+7sc5 z_6g5)w*)vTbn+oUv!nwVr0Lusu7u1ZTFiyY**OS52B2g+D89QeND|k_7aL2dW$rXu zK9b(02_z2!!nq4NsS({ScegZ*3AD5p-qNvPhAR!?;dC$yZAXtGDEz7lT-$ljf z_zSzqN1YsQ3n$|~`i{vrnEIML})a8tg z)C6iB3BhO|@P?Ohc}^4*xo6th#@vll(eK#WoTbd8c7>xC>wY%>e5Nqw)DV+!T|dl3 zCvBRtoE(R{mZ{B*NPsJ{X1(Fl&;DQ2A5p!?q&Pj{C~cLnX}R8MZaeBJGSh=W`re$; zN2?u--0;qi4=t0f35uKhG@}OR@29~w8Dt(CdH2)ldNY}wF@F^K-JE>zqT(v<=b!pY z;uhqsMhpR4BlV{-=%N-h#F7=tkelT-N2fW$w90Cm4&P}V&Sj0OH8!Ph944ZjO&+7h z5iMpmVRv^%9*PK7L5^a68kSP5Ho>)wcl>^i1e46FY?L#qNtZR&%nNw@c+vr0A)+x}B7j#MsuI%$kZh^7<%Z2`MM~DaMIk z4f{*oobv88$*LTN^+m9lVLICSA)gt9DN>drOY1DuVRBB@?&5kX1# zZif&u>7KUx0sm&@vy0CeOk}-?5kg6JzSAA%eO^)(p1_3mpa4aMKjZPj!IR{MDXMKZ zASP&B2MXiNO51xLq6cY3(bm~UQ6JbJOvfdL^yfhz{=xZuZw z$-T2S2R-zFtIe8j=oa_+mZwvV0|55)euW5l6|++!2(mVog_c%j^+>nt^OYb}GyZ_* z2U)1r%1rxC`%*3o!xUm(l+>b=ZMo@Jadu;a_LaTZ5R?1HihNx$+4Z^CCy9y$+qt+3m=Y=z`A7r*Ir(T-LTb~rp= z^E__|Z#vP(nJ?)ggAD3m60 z7iWG~PJ|?vBKdtrLStaTs7psn-}sdb>naxg+bdtG%$)^Gjph=Qar@<)f0pP8=>8a1 zPGy&BpgoJ`=s@2+kg!)w50g_|+^vy>PtVn{bu-AqHq|WL4=+wgFNHCwNLQ3O za1)t+moT;vj+qpmUmy!2G7aXeHScF&@m?(Hj+MLB67vY3X?SAHcPu^~ppc6-&5Q4J z_zHrlRgp;Io27GmDAZl|sHh6fJ#6YXe6`0;R#PjRX zEpk5i1&=-6@>vZ=$~K3wT+%oU@Px}F!8J+s{```jWpG)JH}h9+Ds44afXKRySxrAS zyH-cJhvXsS7A`eVvRhOV{+^-U;p^9S%ivb&)T+f5Odss9@%9)lp6Z0JO7{qf7Zq;@ ze5^F76aU24SXf+r```JZ6{OGOc9%j57hgrBH*`|uQ8IG(yav);f_WQzl)+?lz@o?+ zL}rJ3?CvSX=DpZ|^oDqm7_-|UG|jZB0g&mfG#2q2A0FlFNc5(%!6J9)X`@OysFV(+ zzVlob?&WVqry=(Gh}!t7C7XeM(-d>Gpk}!E zA=>FyGE0lY0(QfeLj>JON-hKpGF+St=Qtz99}bSFQ&j1+20a9VvV=>Lb22t=6k~~& zSi*i=IobkYNU?(`_Q7oI6rBUXbKxX-BMbcjJwa>a4Qr;(%SqXNnZhLKZ=ohubJ`}D zmcEKu>`nF6JdiZs7CUC3*3KA*UigzS+`yEF`1W@Cl+^sk9eYBtjs=|MZe5UH9(d8*?vjM+kO$WH+>S>!-CS&594~(+PWm=i_{<$~DK&}($2(wLiltXE)3?2iDZm)2Z*;JXwkb4$wDxH{ntPF- zr7HUBex4AZa@Llw2aR&WZ7gX+E9R5$-M$7kHPcUXaw9VD;qbIu28b;RMw;)}EIzO8 zn=bqK2l93HlFC+QAaZqSg5>_*NQXB{{5tzRy|V@QyWQLP8nWNj4!vi! zd<-2##_oO&sOAiinnblEz1<-{aeH=u zJ@JQGbc{B;+l|X}{lmlyg7syayaH$}>h$lOuL;!4=%?#)=ex6kf7HbO&4z|ue@4{E zo_nw?@KLHW%RWe>2i2iM?k5zV%*=VTRy=R*gO!!$1UM9NkYqDUrSITuAT6~edP6#X zd{pOncq&1r`YYhI`k^b{-U+s#A?*%z)I3)V4{I@F8A!A01Je743M6K_4>@(;3io;{FDe7JSx;WW#lfnSXhTTrTS{cFHJco~ zy2X;OmZauzf6`Wwi{x;QAY;c%V@qu?RC_v+)b!3QSOqSFaAutHm?UeJ-^o`xEICC* zo1d~@me1LM4Qypr{!C;*g1TdA{C<@x8&p#333Sh7MPpntm_h9p%xUw+@s$>V|b+7XOCBq*t)&J+WTE2y||I-+6 zr{DOX`kyxnrI!VF>D67na0?)c0$sm96?yenS!+dNtU%BK%Oi_ZxH)SCTWYob2I@bLs{1%d-wy} zRa_o&x)V|mXewE;N6VrBk!hhXz@}0ii#^0r-?~6D+8Dst=uVlBU>Obud(0DJ0{CCo z&5a)MtRVMy7ZD3|FMhB*0tn$18r@}o0DMp)vPJ5oF?ccQq)vTjREiEZHAcX14>aQn z$*&wFk*|Sb`MvgYr4GO{cel`w9})~Fxdp!`(P%sce%r%HHmF1pdE|plZ2CzvGJp@Q z3edPn{_2vl@gZJpQqvlYF~A?SdhO?4IT{(D8w)G{0$wVTozGSs<-1G52`43e=jB~B z?*dYxaXo%2psO%42fs3v*F4l`_~M0EuWkhOFlMQB@>N5EhVG@+eQdV8oQ#av^5d~0 zdAEQ1+$3M1a7WIZ>hyy5n34vFZf8ro!ACtHs9Kr-4J;3&@{a^{YZJTg|M(7c(3;z2@M!y{A^roY_M_b82}#IZqG-#_g^oPcLL zi~a_@JI*^SKsdGidrj|YUfXL9@x>F+z&FyHMZ(;;R?~+XGu%##Bgh^MA4%flS;lha z8<3m&=4a2ptQ#=)Z#@s*inEBKt59oC!0nbyKKcgwY&ECw%H$pF@rlLbJD02~+8mj= zr}zqWY3i;bg4m9RjNQvDY6n#nSLpWI29iP7!Qy)iKC9{&TSh5I-;u4WHM;1?2`^%C z67$p5uk^28ajZ~WVc0Djcm~ko-kOCJr(1U0uGX(X%3oh!nrdO0c?8u@F4zt=`7+yW zohYnigEG&!j9?1(FT<^5HB0tpF^!6&4GMZUD=Xnb?DC+P&%Cy;sHNQaFUSn^S{8 z`H7FfVhH@lP8P`{{fj-G-;NJTyxT5o6v>_7in81>-pM83|*ApJd|LJGLed z=qopRNL6S4xWi)cC)4zk)BQ&RIYWrT!1>g*%mu#17603=lsR+IZH|1(05%a_XqE5u^MdGJ%^6* zZxKhI$^FaERls_-l}|(`Sb0xGUv3#P#yBLfb5~Cluzl`a}!WQ57qt&7Rc@6HeA1;CE-W*}IM0s{mkg<`Fq};ee8em+lc!8siu_#hzR!xwavlKx zR+C@syDQsAO#^b6A_ErR&}ydcvUW&5cEkLVe2JsaV=HsuM!-x6%w@SBE@Qj|44
Gg$tYis#Mo)pjEJ2Jt)&Y&{~l-c@-&*>)%|25#x%GbL>ckuH3`vP04A*}+mD)IbHD8{V8Xj!1A_!q`{ zU})pH>)barOF>X$un^70n^=9E$i)gw4orH}Wh40in9>gDpCgndfoV$-9m0-oZ*Cy; zw?%^;-HskJ13=J5i5AZ1=Q4!9YF}}tJ!gpHT9+OXu(_xot{Fk{oTE>Zl30c{;wiRE zA0tU$?({wTna%$OCJg45}8@v{@i9VY3+1(afo!x6g zZL7@vlHkvBd15^0&pq@#%@--M%DYPg!Ljiee3VeQDemnML~B|>Tz5jld;*ad@sh?< zig7~_-1W_D6`nS^q9j5_UhOFv8FW`ktvtXve%C#FqA<}zs`EY>Pu4l{Ep}Ttf0TFDHt*YWud?0rkZ34x^zYxD zXM-Duot(^2Wym1q8Tj2LQea05| zehI!sO(#(Hxn)C-;kFRxIxyMr2- zvR&b}<24W+>ex4YeYdWzXsqS*{dSO?fGylTNeZ=uT~B)Fp!TZUZdO`)H!K9PZB308 z8Ta;(%XsUfu65Uexvyq9td9HMyr_A^F15(|v4C;c^Y}WxC?nsgLGH|fQ`&n}^R@5O zqWmj=9&}i_^{J%h(K8{ss6KrBbE|{e5?v};Kc=sc)Pbw*#qqj~lU9_y)JlV$uCH~& z`>OhV#Fu>8DBr;q?x`;wrTmlE-a2_?c6$!Wq_xznEPP^s`Ea3IYBdy`OJ^#FPp}aI znL)V1T7FL6>4&+MlV}ndxJLEmT+59wd0K@(6Ya4}Efu&29&pOoPJVR^*wb4Pmf-ia zUUg$HZyL47*GMk7q7-O9@x>~-VaC7wQefrBI}U2NIv3iv$=9pKQRrW(Wpk77r1&^r z{a{0cSG09Agd^2na)-k~=f|ODT2Db%sGclu$EfMnrl68H*WdI$Xgo!=$PQMS;0;>c zB%5s~b?o|l=v~rSeKY&O?brivNC%m5Me@O;>liWzM?M^7Um6v6}4-(Jg%`F>~k;xjVTtj9m zmGLjiHjUe;_4jqy>8x#L@qKVA9{cZ$2$dd}jug#q| zti%!e@QE*P;p_8c4}m*3z0pa6yT|f_b73NDnibn)w$GC!&3eJx)h;DUeUro<*K5of z6GzJ(<21>izdE}L{~L_HdhnImh1>54Rl z3OC@aPo^MYfz`GZo-Ym0C09E~8Au*Ex{u^7*0em6Hvj!zlj`tSbM6gV=+2p)6F_%m=7#oUbpR{pLeEAzU=b~25sOr2Oy>3aoIpBD5 zKHqIPmQv@IJkHzQ_?=iG?stc`WE$zdpxP#Oo-gLo!-D8Gu9=!{>125Qc{7mHYg}|= zt9PCr?t$2^f^smqc7wj5oZ8Wt*Q>%o#4EQ#OAIJvI7mf136=bYanI~I&s zu%sNvnw?y~%ln1px(y3^RKb?R${JRxO_~}!$j-Ai@^-`E8?GxSGW~Bik)%Z1>}6*7 zdQayk7_uDfw_5DeUykn_8(3~Td7e+KvmtVqHQrllOuZZkAW>HsRcg*M%f_|cF7W|e;AFC`wEV%)`|dTFE2lZsKX#wTv~!{TH|+TvD!_2Z)!PW&{~UCt zH+I2n)|yOGu0NaJ%gkV^)Nb}`ZPtb|=aVyB<(3`KmV{^ENDLbzU*!!$wT-bXH#=;pN!iS5yOT$Cw4#uDgAamf)`6 zugP%D6aEcNw~126twnq857lAcd=2C$nqK45-C5VYHrP}!ivFf7Q#H!8QQ1GLfnDl5 z@}5b3>gUE6U!tWs35qv=*YukoTh}l*%*E5|cWoi%uY2c>x_?E{LB&QYOXWiI?t0lA zO}S*sJ?qw~kD9}IzCCAc;~Ll;L~Y~P?9pd#KL)%Uv`SLZXO765WLrqW@T;bu{T(DK zqy!!9b|qVT186_R|85ILe6I-l|Z^Wq`ahBx}x~FVB=I2mFSNz z_WSFUMoV?%VDlu>e{fb|L1#LSjSaPF7ZK=w=(t?Nd>VZvNa{qUBlOfo+ z56ejixLmc`=;T_08$5qyrd^C14_u(`wr&45VzP%FfhHH5_XZoV1ney`8&BSA-nb39 zxm4EgA$WD(LD4fus=R)F#FYZ%Cv)Y_(JMm{*iiQ6B5&KQ*-raqI@3k1I1r`uV2xNx zgdUdRq;+=D$xzr(1P+v)Fluz@sIJS6XNwDE!&gf}~0RhzE~h zNw7R-Q6~Zj{x~~y(kMbJ7nbx)UUmJD{c+>&M+mxoe`=j={ZQ!}EGb~$IyJQSL52_^ zboYiv$?;f}yJ%9dBVpx}yy)Wxl%h_Q5L{Ya$|a|$SfqDdSF?!|k(+8!yW0Ar#Rh_W zG`9{#=%Pdi)^*~V1Q1_Jh9Umb1pIU%5V;z)$Cp_;habp2Hk(<>g}y!|Ep2VfV-yle zBkogdld@N|8w5ewVt14uSrp4hAvZPW+x%N~Cecbt^*9Y`S&{`N?Ex81Uxb|3Zfo&- zH0{d3pzDeP<_rSZpal6;vFyiebNxuwty}a93;Iqe_rfyGesFnpAHiS{#oH!Ax4~@g zjFqQ71s;K+rE<*Hc>L_}fQIdo00MlfE;p z-^ZFM(dAx^vdBB$aVa>Mu1RMAd-BObI#dhI(?bze>*}0>$&3h@P1tztZt0L;vvW_+ z(%_2`2GI9)gry!r5Z~*jnMmRN8x=P^bX{MK`Y-BXm$qO07GALxdmKMlc?rqqPIWqh zHp7PcUQh9C22T%UH(kmI=(^_+aiT0-2W^NpqMyg}IUgnu#`2CR57NY>9lzwT#Dcr# z&7*A*v3V6cuT4D}SzYEXMROt)GmhkVoe!UA`FD9+tyO(ZyYN>WuhHiTvrQ@z%Hr*n4+)e;NeH!J>7R#3xG7Wc$K_BaNcW@qut!z?3@TZOGxYl4k iKx2XVAbC*a41bwa@{t?0*cAx;Q{Qm&gQ=&is diff --git a/app/screenshots/sideplay_sign_in.png b/app/screenshots/sideplay_sign_in.png deleted file mode 100644 index 6a66536466a5db7b9da845304a3dc50f50335408..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33435 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 Unit)? = null, onClickLabel: String? = null, contentAlignment: Alignment = Alignment.Center, - content: @Composable BoxScope.() -> Unit + content: @Composable BoxScope.() -> Unit, ) { Box( modifier = modifier @@ -55,16 +55,13 @@ fun SidekitTile( .clip(SidekitTheme.shapes.item) .background(color) .then( - if (onClick != null) { - Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) - } else { - Modifier - } + if (onClick != null) Modifier.clickable(onClickLabel = onClickLabel, role = Role.Button, onClick = onClick) + else Modifier, ), contentAlignment = contentAlignment, content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - } + }, ) } @@ -89,18 +86,18 @@ fun SidekitItem( colour: ItemColour = ItemColour.Primary, titleDecoration: TextDecoration? = null, onClick: (() -> Unit)? = null, - leading: (@Composable RowScope.() -> Unit)? = null + leading: (@Composable RowScope.() -> Unit)? = null, ) { SidekitTile( modifier = modifier.fillMaxWidth(), color = colour.container(), onClick = onClick, - contentAlignment = Alignment.CenterStart + contentAlignment = Alignment.CenterStart, ) { Row( modifier = Modifier.padding(horizontal = SidekitTheme.spacing.itemPadding), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap) + horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemContentGap), ) { leading?.invoke(this) if (icon != null) SidekitIconImage(icon) @@ -110,7 +107,7 @@ fun SidekitItem( secondary, Modifier.weight(1f), color = SidekitTheme.colors.surfaceA6, - textAlign = if (secondaryToEnd) TextAlign.End else null + textAlign = if (secondaryToEnd) TextAlign.End else null, ) } // The arrow marks a row that opens another page. With a secondary value it must be @@ -118,7 +115,7 @@ fun SidekitItem( if (showLinkArrow && (secondary == null || showLinkArrowWithSecondary)) { Box( if (secondary == null) Modifier.weight(1f) else Modifier, - contentAlignment = Alignment.CenterEnd + contentAlignment = Alignment.CenterEnd, ) { SidekitIconImage(SidekitIcon.ArrowRightAlt) } @@ -136,7 +133,7 @@ fun SidekitIconItem( colour: ItemColour = ItemColour.Primary, color: Color = colour.container(), iconSize: Dp = SidekitTheme.spacing.iconSize, - onClick: (() -> Unit)? = null + onClick: (() -> Unit)? = null, ) { SidekitTile(modifier = modifier, color = color, onClick = onClick, onClickLabel = contentDescription) { SidekitIconImage(icon, size = iconSize, contentDescription = contentDescription) @@ -149,7 +146,7 @@ fun SidekitItemRow(modifier: Modifier = Modifier, content: @Composable RowScope. Row( modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(SidekitTheme.spacing.itemOuterGap), - content = content + content = content, ) } @@ -159,7 +156,7 @@ fun SidekitCard( modifier: Modifier = Modifier, color: Color = SidekitTheme.colors.surfaceA3, onClick: (() -> Unit)? = null, - content: @Composable BoxScope.() -> Unit + content: @Composable BoxScope.() -> Unit, ) { Box( modifier = modifier @@ -169,6 +166,6 @@ fun SidekitCard( .padding(SidekitTheme.spacing.itemPadding), content = { CompositionLocalProvider(LocalSidekitContentColor provides SidekitTheme.colors.contentColorFor(color)) { content() } - } + }, ) } From 7a59e7fbe8628e54a3ef6dc8cf0b44c48176e531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 15:55:17 +0100 Subject: [PATCH 40/46] feat(sideplay): cache audio on disk and drop the streaming-only notice Enable librespot's bounded audio cache (1 GiB, LRU eviction) in the native session and point it at the app cache directory. Clearing the cache now drops both artwork and audio. Remove the Settings pill that advertised offline downloads as unavailable. --- .../apps/spotify/SpotifySettingsScreens.kt | 26 ++++--------------- .../sidekit/apps/spotify/SpotifyViewModel.kt | 3 ++- .../apps/spotify/bridge/NativeBridge.kt | 5 ++++ .../apps/spotify/bridge/SpotifyNative.kt | 1 + .../apps/spotify/data/SpotifySession.kt | 10 +++++++ .../apps/spotify/NativeSpotifyCatalogTest.kt | 1 + .../apps/spotify/NativeSpotifyGatewayTest.kt | 1 + native/sidekit-spotify/src/engine.rs | 26 ++++++++++++++++++- native/sidekit-spotify/src/lib.rs | 12 +++++++++ 9 files changed, 62 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt index 3d38c017..46fbd176 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifySettingsScreens.kt @@ -95,27 +95,11 @@ fun SpotifySettingsScreen( onClick = onAudioQuality ) } - item(key = "streaming") { - SidekitCard( - Modifier.listFade(listState, 1).fillMaxWidth(), - color = SidekitTheme.colors.surfaceA2 - ) { - Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { - SidekitText("Streaming only", style = SidekitTheme.typography.bodySmall) - SidekitText( - "Offline downloads are not available in this build", - style = SidekitTheme.typography.caption, - color = SidekitTheme.colors.surfaceA6, - maxLines = 2 - ) - } - } - } item(key = "cache") { SidekitItem( - "Artwork cache", - Modifier.listFade(listState, 2), - secondary = "12 MB", + "Cache", + Modifier.listFade(listState, 1), + secondary = "1 GB", secondaryToEnd = true, showLinkArrow = true, showLinkArrowWithSecondary = true, @@ -125,7 +109,7 @@ fun SpotifySettingsScreen( item(key = "history") { SidekitItem( "Clear history", - Modifier.listFade(listState, 3), + Modifier.listFade(listState, 2), secondary = "${state.history.size} tracks", secondaryToEnd = true, showLinkArrow = true, @@ -136,7 +120,7 @@ fun SpotifySettingsScreen( item(key = "account") { SidekitItem( "Account", - Modifier.listFade(listState, 4), + Modifier.listFade(listState, 3), secondary = if (state.signedIn) "Signed in" else "Not signed in", secondaryToEnd = true ) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index f1efb2b7..47f27ef6 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -696,7 +696,8 @@ class SpotifyViewModel( fun clearCache() { clearArtworkCache() - _state.update { it.copy(notice = "Artwork cache cleared") } + session?.clearAudioCache() + _state.update { it.copy(notice = "Cache cleared") } } fun moveToNext(item: SpotifyMediaItem) { diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt index 8cd190fb..f0f8d866 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/NativeBridge.kt @@ -17,6 +17,10 @@ object NativeBridge : SpotifyNative { if (isAvailable) runCatching { nativeSetTmpDir(path) } } + override fun setCacheDir(path: String, sizeLimitBytes: Long) { + if (isAvailable) runCatching { nativeSetCacheDir(path, sizeLimitBytes) } + } + override fun connect(accessToken: String): Boolean = isAvailable && runCatching { nativeConnect(accessToken) }.getOrDefault(false) override fun setAccessToken(accessToken: String): Boolean = isAvailable && runCatching { nativeSetAccessToken(accessToken) }.getOrDefault(false) @@ -65,6 +69,7 @@ object NativeBridge : SpotifyNative { private external fun nativeInit(): Boolean private external fun nativeSetTmpDir(path: String) + private external fun nativeSetCacheDir(path: String, sizeLimitBytes: Long) private external fun nativeConnect(accessToken: String): Boolean private external fun nativeSetAccessToken(accessToken: String): Boolean private external fun nativeDisconnect() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt index bcae390b..b435fe27 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/bridge/SpotifyNative.kt @@ -8,6 +8,7 @@ interface SpotifyNative { fun initialize(): Boolean fun setTmpDir(path: String) + fun setCacheDir(path: String, sizeLimitBytes: Long) fun connect(accessToken: String): Boolean fun setAccessToken(accessToken: String): Boolean fun disconnect() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 9a4f6d9d..2b6ff6ee 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -34,6 +34,7 @@ class SpotifySession( val signedIn: StateFlow = _signedIn.asStateFlow() private val appContext = context?.applicationContext + private val audioCacheDir = appContext?.let { File(it.cacheDir, "spotify-audio") } private val settingsStore = appContext?.let { SpotifySettingsStore(it) } // Legacy file names kept so existing dev installs keep their queue and history. @@ -84,6 +85,7 @@ class SpotifySession( init { if (nativeAvailable) { tmpDir?.let(NativeBridge::setTmpDir) + audioCacheDir?.let { NativeBridge.setCacheDir(it.absolutePath, AUDIO_CACHE_LIMIT_BYTES) } NativeBridge.initialize() } } @@ -202,9 +204,17 @@ class SpotifySession( /** One librespot player event, or null when nothing has happened since the last poll. */ fun pollPlaybackEvent(): String? = if (nativeAvailable) NativeBridge.pollEvent() else null + /** Drops the on-disk audio cache. librespot re-fetches anything it still needs. */ + fun clearAudioCache() { + audioCacheDir?.deleteRecursively() + } + private companion object { /** Slightly longer than the native connect timeout, so its specific error wins. */ const val CONNECT_TIMEOUT_MS = 25_000L + + /** Upper bound for the audio cache; librespot evicts least-recently-used entries. */ + const val AUDIO_CACHE_LIMIT_BYTES = 1L shl 30 } private object NoopCredentialStore : hrln.interactive.sidekit.apps.spotify.auth.SpotifyCredentialStore { diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt index d02eebce..44eb6650 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt @@ -139,6 +139,7 @@ class NativeSpotifyCatalogTest { override fun initialize() = true override fun setTmpDir(path: String) = Unit + override fun setCacheDir(path: String, sizeLimitBytes: Long) = Unit override fun connect(accessToken: String) = true override fun setAccessToken(accessToken: String) = true override fun disconnect() = Unit diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt index 16896140..3f723be8 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyGatewayTest.kt @@ -107,6 +107,7 @@ class NativeSpotifyGatewayTest { override fun initialize() = isAvailable override fun setTmpDir(path: String) = Unit + override fun setCacheDir(path: String, sizeLimitBytes: Long) = Unit override fun connect(accessToken: String): Boolean { connects++ token = accessToken diff --git a/native/sidekit-spotify/src/engine.rs b/native/sidekit-spotify/src/engine.rs index e65d08d8..ab126c83 100644 --- a/native/sidekit-spotify/src/engine.rs +++ b/native/sidekit-spotify/src/engine.rs @@ -2,6 +2,7 @@ use crate::audio::{PcmSink, CHANNELS, SAMPLE_RATE}; use librespot_core::authentication::Credentials; +use librespot_core::cache::Cache; use librespot_core::config::SessionConfig; use librespot_core::session::Session; use librespot_core::SpotifyUri; @@ -76,6 +77,7 @@ fn audio_rx() -> &'static Mutex>> { static AUDIO_TX: OnceLock>> = OnceLock::new(); static TMP_DIR: OnceLock>> = OnceLock::new(); +static CACHE: OnceLock>> = OnceLock::new(); pub fn set_tmp_dir(path: &str) { let slot = TMP_DIR.get_or_init(|| Mutex::new(None)); @@ -84,6 +86,28 @@ pub fn set_tmp_dir(path: &str) { } } +/// Enables librespot's on-disk audio cache. The cache stores the encrypted CDN blobs and is +/// bounded by `size_limit` bytes; librespot evicts least-recently-used entries past the limit. +pub fn set_cache_dir(path: &str, size_limit: u64) { + let slot = CACHE.get_or_init(|| Mutex::new(None)); + if let Ok(mut guard) = slot.lock() { + *guard = Some((PathBuf::from(path), size_limit)); + } +} + +fn cache() -> Option { + let (path, size_limit) = CACHE + .get() + .and_then(|slot| slot.lock().ok().and_then(|guard| guard.clone()))?; + match Cache::new::(None, None, Some(path), Some(size_limit)) { + Ok(cache) => Some(cache), + Err(error) => { + log::warn!("audio cache unavailable: {error}"); + None + } + } +} + fn tmp_dir() -> PathBuf { TMP_DIR .get() @@ -132,7 +156,7 @@ pub fn connect(access_token: &str) -> Result<(), String> { tmp_dir: tmp_dir(), ..Default::default() }; - let session = Session::new(config, None); + let session = Session::new(config, cache()); // A fresh TCP handshake has no deadline of its own, and `Session::connect` may walk up to six // access points. Bound it so a bad network cannot wedge the caller (and the UI waiting on it) // indefinitely; the caller retries with a new session on the next play. diff --git a/native/sidekit-spotify/src/lib.rs b/native/sidekit-spotify/src/lib.rs index 9cdaf3bc..5bc43e22 100644 --- a/native/sidekit-spotify/src/lib.rs +++ b/native/sidekit-spotify/src/lib.rs @@ -50,6 +50,18 @@ pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeB } } +#[no_mangle] +pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeSetCacheDir( + mut env: JNIEnv, + _this: JObject, + path: JString, + size_limit: jlong, +) { + if let Some(path) = string(&mut env, &path) { + engine::set_cache_dir(&path, size_limit.max(0) as u64); + } +} + #[no_mangle] pub extern "system" fn Java_hrln_interactive_sidekit_apps_spotify_bridge_NativeBridge_nativeConnect( mut env: JNIEnv, From 6600024ba9dd913afb192bc77454b2355c49983d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 15:23:00 +0100 Subject: [PATCH 41/46] feat(sideplay): add autoplay and tidy home controls --- app/screenshots/sideplay_home.png | Bin 0 -> 52293 bytes app/screenshots/sideplay_settings.png | Bin 0 -> 29628 bytes .../sidekit/apps/spotify/SpotifyGraph.kt | 1 + .../apps/spotify/SpotifyLibraryScreens.kt | 8 ++- .../apps/spotify/SpotifySettingsScreens.kt | 18 +++++- .../sidekit/apps/spotify/SpotifyViewModel.kt | 57 +++++++++++++++++- .../spotify/data/SpotifyQueueRepository.kt | 4 +- .../apps/spotify/data/SpotifySession.kt | 5 ++ .../apps/spotify/data/SpotifySettings.kt | 11 +++- .../apps/spotify/SideplayScreenshotTest.kt | 1 + .../spotify/SpotifyQueueRepositoryTest.kt | 8 +++ 11 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 app/screenshots/sideplay_home.png create mode 100644 app/screenshots/sideplay_settings.png diff --git a/app/screenshots/sideplay_home.png b/app/screenshots/sideplay_home.png new file mode 100644 index 0000000000000000000000000000000000000000..f10e670365a7dc427f86c0a2c360a02069ff3bcf GIT binary patch literal 52293 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 literal 0 HcmV?d00001 diff --git a/app/screenshots/sideplay_settings.png b/app/screenshots/sideplay_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..a5f09264a400f4da8a478c76eb2ab4a28afeb4d5 GIT binary patch literal 29628 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ƽ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 Unit, onAudioQuality: () -> Unit, + onAutoplay: () -> Unit, onClearCache: () -> Unit, onClearHistory: () -> Unit, onSignOut: () -> Unit, @@ -95,10 +96,21 @@ fun SpotifySettingsScreen( onClick = onAudioQuality ) } + item(key = "autoplay") { + SidekitItem( + "Autoplay", + Modifier.listFade(listState, 1), + secondary = if (state.autoplay) "On" else "Off", + secondaryToEnd = true, + showLinkArrow = true, + showLinkArrowWithSecondary = true, + onClick = onAutoplay + ) + } item(key = "cache") { SidekitItem( "Cache", - Modifier.listFade(listState, 1), + Modifier.listFade(listState, 2), secondary = "1 GB", secondaryToEnd = true, showLinkArrow = true, @@ -109,7 +121,7 @@ fun SpotifySettingsScreen( item(key = "history") { SidekitItem( "Clear history", - Modifier.listFade(listState, 2), + Modifier.listFade(listState, 3), secondary = "${state.history.size} tracks", secondaryToEnd = true, showLinkArrow = true, @@ -120,7 +132,7 @@ fun SpotifySettingsScreen( item(key = "account") { SidekitItem( "Account", - Modifier.listFade(listState, 3), + Modifier.listFade(listState, 4), secondary = if (state.signedIn) "Signed in" else "Not signed in", secondaryToEnd = true ) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt index 47f27ef6..8b1198c8 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyViewModel.kt @@ -75,6 +75,7 @@ data class SpotifyUiState( val searchScopes: Set = SearchScope.entries.toSet(), val query: String = "", val audioQuality: AudioQuality = AudioQuality.High, + val autoplay: Boolean = true, val notice: String? = null, val undo: SpotifyUndo? = null ) { @@ -102,6 +103,7 @@ class SpotifyViewModel( val undoWindow = UndoWindow(viewModelScope) { _state.update { it.copy(undo = null) } } private var searchJob: Job? = null + private var autoplayJob: Job? = null private val savedTrackTouched = mutableSetOf() private val savedCollectionTouched = mutableSetOf() private val followedArtistTouched = mutableSetOf() @@ -119,7 +121,9 @@ class SpotifyViewModel( } viewModelScope.launch { it.settings.collect { settings -> - _state.update { state -> state.copy(audioQuality = settings.audioQuality) } + _state.update { state -> + state.copy(audioQuality = settings.audioQuality, autoplay = settings.autoplay) + } } } it.playback.onNext = nextHandler @@ -687,6 +691,12 @@ class SpotifyViewModel( session?.setAudioQuality(next) } + fun toggleAutoplay() { + val enabled = !_state.value.autoplay + _state.update { it.copy(autoplay = enabled) } + session?.setAutoplay(enabled) + } + fun clearHistory() { history.clear() val snapshot = queue.clearHistory() @@ -1127,7 +1137,7 @@ class SpotifyViewModel( } if (!hasNext && !auto) return if (!hasNext) { - _state.update { it.copy(isPlaying = false, positionMs = 0L) } + if (_state.value.autoplay) autoplaySimilar(current) else stopAtQueueEnd() return } val snapshot = queue.next(auto = true) @@ -1140,6 +1150,49 @@ class SpotifyViewModel( } } + /** + * The queue ran out with Autoplay on. Ask the catalog for the current artist's top tracks, + * drop anything already in the queue or history, and let the normal advance path start the + * first one. The guard keeps a slow request from hijacking a track the user started meanwhile. + */ + private fun autoplaySimilar(current: SpotifyMediaItem) { + if (autoplayJob?.isActive == true) return + val track = current as? SpotifyTrack + val artistId = track?.artistId?.takeIf { it.isNotBlank() } + if (track == null || artistId == null) { + stopAtQueueEnd() + return + } + autoplayJob = viewModelScope.launch { + try { + val artist = SpotifyArtist(artistId, track.artist, 0, emptyList()) + val page = catalog.topTracks(artist, 0, SPOTIFY_PAGE_SIZE) + if (_state.value.current?.id != track.id || !_state.value.autoplay) return@launch + val known = buildSet { + add(track.id) + addAll(_state.value.history.map { it.id }) + addAll(_state.value.upcoming.map { it.id }) + } + val similar = page.items.filter { it.id !in known && it.playable } + if (similar.isEmpty()) { + stopAtQueueEnd() + return@launch + } + val tail = if (_state.value.shuffle) similar.shuffled() else similar + applyQueue(queue.addToQueue(tail), syncNowPlaying = false) + advance(auto = true, forceContextLoop = false) + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + if (_state.value.current?.id == track.id) stopAtQueueEnd() + } + } + } + + private fun stopAtQueueEnd() { + _state.update { it.copy(isPlaying = false, positionMs = 0L) } + } + /** Starts a real Spotify item; local test fixtures have no URI and keep the local simulation. */ private fun startRemote(item: SpotifyMediaItem, positionMs: Long) { val uri = item.uri ?: return diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt index d458db87..22ab250d 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifyQueueRepository.kt @@ -171,7 +171,9 @@ class SpotifyQueueRepository( return commit(snapshot.copy(upcoming = listOf(item) + remaining)) } - fun addToQueue(item: SpotifyMediaItem): Snapshot = commit(snapshot.copy(upcoming = snapshot.upcoming + item)) + fun addToQueue(item: SpotifyMediaItem): Snapshot = addToQueue(listOf(item)) + + fun addToQueue(items: List): Snapshot = commit(snapshot.copy(upcoming = snapshot.upcoming + items)) fun removeUpcoming(item: SpotifyMediaItem): Snapshot = commit(snapshot.copy(upcoming = snapshot.upcoming.filterNot { it.id == item.id })) diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt index 2b6ff6ee..4b3e49a2 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySession.kt @@ -151,6 +151,11 @@ class SpotifySession( settingsStore?.write(_settings.value) } + fun setAutoplay(enabled: Boolean) { + _settings.update { it.copy(autoplay = enabled) } + settingsStore?.write(_settings.value) + } + fun togglePlayback() { if (playback.isActive) { playback.pause() diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt index 0cbc9bb7..8c7b24f4 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt @@ -6,7 +6,9 @@ import hrln.interactive.sidekit.apps.spotify.AudioQuality /** User preferences that survive process death and apply before the native player is created. */ data class SpotifySettings( - val audioQuality: AudioQuality = AudioQuality.High + val audioQuality: AudioQuality = AudioQuality.High, + /** Keep playing similar tracks when the queue runs out. */ + val autoplay: Boolean = true ) { val bitrateKbps: Int get() = audioQuality.bitrateKbps } @@ -28,7 +30,10 @@ class SpotifySettingsStore( val quality = AudioQuality.entries .firstOrNull { it.name == value.optString(KEY_QUALITY) } ?: AudioQuality.High - return SpotifySettings(audioQuality = quality) + return SpotifySettings( + audioQuality = quality, + autoplay = value.optBoolean(KEY_AUTOPLAY, true) + ) } @Synchronized @@ -36,10 +41,12 @@ class SpotifySettingsStore( settings.write( org.json.JSONObject() .put(KEY_QUALITY, value.audioQuality.name) + .put(KEY_AUTOPLAY, value.autoplay) ) } private companion object { const val KEY_QUALITY = "audio_quality" + const val KEY_AUTOPLAY = "autoplay" } } diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt index 9af9146f..19a1b19d 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SideplayScreenshotTest.kt @@ -170,6 +170,7 @@ class SideplayScreenshotTest { state, onBack = {}, onAudioQuality = {}, + onAutoplay = {}, onClearCache = {}, onClearHistory = {}, onSignOut = {}, diff --git a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt index f4fe7326..bfa7f7e5 100644 --- a/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt +++ b/app/src/test/java/hrln/interactive/sidekit/apps/spotify/SpotifyQueueRepositoryTest.kt @@ -60,6 +60,14 @@ class SpotifyQueueRepositoryTest { assertEquals(listOf("b", "c", "d", "z"), queue.snapshot.upcoming.map { it.id }) } + @Test fun `adding several items appends them in order`() { + val queue = SpotifyQueueRepository(Random(1)) + queue.play(tracks, 0) + queue.addToQueue(listOf(track("x"), track("y"))) + + assertEquals(listOf("b", "c", "d", "x", "y"), queue.snapshot.upcoming.map { it.id }) + } + @Test fun `next moves the current item into history`() { val queue = SpotifyQueueRepository(Random(1)) queue.play(tracks, 0) From d99b2d7cb4ff8bee1b950a82eec1a73d5ee4b2a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 15:28:52 +0100 Subject: [PATCH 42/46] fix(sideplay): inset the library label and move settings to options --- app/screenshots/sideplay_home.png | Bin 52293 -> 51151 bytes .../sidekit/apps/spotify/SpotifyComponents.kt | 6 +++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/screenshots/sideplay_home.png b/app/screenshots/sideplay_home.png index f10e670365a7dc427f86c0a2c360a02069ff3bcf..123e2c67c30518cb91865e042a162ce56321e539 100644 GIT binary patch delta 25437 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 Mc%WW+$1d#u0h|WO-~a#s diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index ec7bc364..7bfe8c90 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -235,7 +235,11 @@ fun NowPlayingTile( @Composable fun LibraryTile(onClick: () -> Unit, modifier: Modifier = Modifier) { SidekitTile(modifier, onClick = onClick, onClickLabel = "Library") { - Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(2.dp)) { + Column( + Modifier.padding(horizontal = 4.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { SidekitIconImage(SidekitIcon.PhotoLibrary, size = SidekitTheme.spacing.tileIconSize) SidekitText("Library", style = SidekitTheme.typography.bodySmall) } From 0c416cf1d6be3b78539ddf964171a8e2a4052cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 15:44:27 +0100 Subject: [PATCH 43/46] fix(sideplay): make the library tile an emphasized icon --- app/screenshots/sideplay_home.png | Bin 51151 -> 51331 bytes .../sidekit/apps/spotify/SpotifyComponents.kt | 20 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/screenshots/sideplay_home.png b/app/screenshots/sideplay_home.png index 123e2c67c30518cb91865e042a162ce56321e539..f67de65762676db02166159919aaf80741f6931b 100644 GIT binary patch literal 51331 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 CSwD;b diff --git a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt index 7bfe8c90..adb3db45 100644 --- a/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt +++ b/app/src/main/java/hrln/interactive/sidekit/apps/spotify/SpotifyComponents.kt @@ -234,15 +234,17 @@ fun NowPlayingTile( @Composable fun LibraryTile(onClick: () -> Unit, modifier: Modifier = Modifier) { - SidekitTile(modifier, onClick = onClick, onClickLabel = "Library") { - Column( - Modifier.padding(horizontal = 4.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - SidekitIconImage(SidekitIcon.PhotoLibrary, size = SidekitTheme.spacing.tileIconSize) - SidekitText("Library", style = SidekitTheme.typography.bodySmall) - } + SidekitTile( + modifier, + color = SidekitTheme.colors.emphasisA1, + onClick = onClick, + onClickLabel = "Library" + ) { + SidekitIconImage( + SidekitIcon.Album, + size = SidekitTheme.spacing.tileIconSize, + contentDescription = "Library" + ) } } From 97cca02b85aafb613e4dd1e6aecc58ce44a52049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Tue, 10 Nov 2026 16:00:24 +0100 Subject: [PATCH 44/46] chore(sideplay): refresh screenshots and apply formatting --- app/screenshots/sideplay_album.png | Bin 0 -> 41356 bytes app/screenshots/sideplay_artist.png | Bin 0 -> 50204 bytes app/screenshots/sideplay_library_albums.png | Bin 0 -> 52131 bytes app/screenshots/sideplay_now_playing.png | Bin 0 -> 51838 bytes app/screenshots/sideplay_queue.png | Bin 0 -> 50727 bytes app/screenshots/sideplay_search.png | Bin 0 -> 45126 bytes app/screenshots/sideplay_settings.png | Bin 29628 -> 29269 bytes app/screenshots/sideplay_sign_in.png | Bin 0 -> 33435 bytes .../apps/spotify/SpotifyLibraryScreens.kt | 3 +- .../sidekit/design/components/Item.kt | 31 ++++++++++-------- 10 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 app/screenshots/sideplay_album.png create mode 100644 app/screenshots/sideplay_artist.png create mode 100644 app/screenshots/sideplay_library_albums.png create mode 100644 app/screenshots/sideplay_now_playing.png create mode 100644 app/screenshots/sideplay_queue.png create mode 100644 app/screenshots/sideplay_search.png create mode 100644 app/screenshots/sideplay_sign_in.png diff --git a/app/screenshots/sideplay_album.png b/app/screenshots/sideplay_album.png new file mode 100644 index 0000000000000000000000000000000000000000..fd44f15b6c763893171bb9a97878438b87015ae6 GIT binary patch literal 41356 zcmeFZbyJ+(vo6fwF2M#5kl-5JgF~4L4@Z$64DP$ z5s~*FKKvXYfWs8Vd)Emc1tzcx=-{Lxs;oldobfzOEfdWsXnJzWnpt5}7` z>MStr?ArSxM6X(ogWAo4+HI8dDk=aB(FcYM@-uY2{j>%1|33f!vIDK%v>aEs?^A;> z_Ui@NE4kopRc!duk_IJuyujtvRqsZ3Xs_n#CEmrd<9K-c>r*@a+SBS?&oFa4L)*is zZ3MoKn=t=H5B6qGB-6W_o14B18@)6()OaS5j~%XS-VNEO^X)HnX12Cmn^fJ+t2gD1 zj<1{e?JS!#F{A=?FVA~I{k^Z}p+Z(YS1@6Lr!izgHa+T6pJmCLgun+1Tc@Bw@Sg8W zf!ny}D&^zjW7O3mmQfnB7#`@AwdNkN6{_3&P$}!vCRHFl;AKEk@Etz>DOdYLG}br< zo>Hgt%fp@w-^Zi(J@Y!AW%fGs0ajPR0|t*mCV12+B7TbWJa@~E(f2y~0CarURrdl# zO1U|K%K^&12U_9!2N9#HXEL_PBslU8JE7+d4Dk=|SYs{%@Bty~(_Lh6?9qD&i%gAl z^z_DS#qySxC0NWES`LFBC0@G0w+-#MWZ-?t-tVf~A0;Fujjz}r%`cuJ=*xd_KAye4 z*m@*8xvn@T*^^|v2YHeIJb5(?8RSHf@Y|Tckcxei0pGCVl6HR)>&t)=q2RTNEsb$3 z%>@ycnwGXzj)2gadok5}I9t+N;d4XpA$@UNGagZ2&q*l=X9(YR9?U$^-FxQy>dE@) zlR?isQt>E0#o~*QNwQTw?KHvadDT)Oy9P&)e17yP_ zuH~-}S+8*%R)k4|g8z!cwg9}m+_f{O0g9Sm9*?38S3026i5GG7(vx_v@21>rI@9$I ze}Z52r)2(HmhW{gS-A7j90A55v+u+3A9z3&6tyHL-2L^z^!2w(U%x&_8AZjo$*kv2 z44;Xxexod_S28x)dZaq2*JD$m_!r2TDx?c(eacl*_lu`YgZA2r=9-cT|M~MBWhVZF z_3!4rEZ^0z?*Di$cNf}s#^B{3>xGsH)w>%3otR~8L#!SSIPDrk%tN93e)Vy#VMuYg zVa=y`q24+TYaS&WnF$e3#o1%!NQZj$w!R@el*~tM*s**pzG)y~6x-hvc3v)Nw5(&_K^CL=tnF!=!_L;tWO5|- z6^l>^EBqxkqr>c8hPhQ)o>A%H`B1f8<9M-#y66HLH6BTXu|us_Dp=U+z_0Yo=dL-^ zLQ)5gdl4FXm@FqG<{I?Z7d-F#>n<`ti9`~CY_U~uArB>32$^zeyO^&u!|Q64i)e4= z0b(y149A_R$%cr!%%bj}KiPHiD~Ca1Mxb2_PADt%3+dTi`D~0A+t*v$=sg8j-^b~! z9@Vx>T>HZr+ATM}eSyD&N03;c<#l4I8y|RZOx#pR>icY;DbjZ@UkI^Z%(Yz!Kplhs z+|QnrubRB0Vb!DLLJDNj9DJd; zZs1R+((Hbg!FLeXTYCksHQKTJZ)~A9iFPKL*~5wF!vQ(sjEGepx@MA%Pz=MS+nN$YtTn($tVnt;wNL&WNi`Oh_`n?-zvGzRqDLd1hJ{ApSt({ zruUg=7JmUW2h!mW6#?|d`_0H`$B!_NjIa0oLQ^tdLZ7`3iwb%MKKDQCsK{`%F@`)~`zKg4};oK;aGSte>MuyR&{7J-@$(wwjF!?z+YBy%*Ku_oC-ioYu&P1#%!CuHN@?5nhyb zw5Yazp36w;y5C9Cb*bx`UTyV^79sOKV$X~rgGQZbut?%kLBAC%yKFitcl?E~0@22b zvws-IyMCFS8YYaFgg7{fl2G9vJ?vXf?Z|9&>c2kCk?72o>g*Cw#WP3&;9~`Dfzfnn z^5*;*vWRxBt__O&ZrZl@tDa}kd^$W0!K|yd4Yq4GJvA`8*i+g$Ugt7?ox7uHy-97? zA3Yv|&^d+KDu0cpXS-WEKa12ql<3?PH@ca*xbXEsPwW~*QD@uZTxXVEJ`Pf6MMZA8 z&}w**N1{j7lyI=g=BrggM@?40K99c=2>X#riUj2Y)1GnkdOHIVcIJN^Tg1qtYvb9V=%zMAj_$g#b~ygFqjy8T zc0WRQSJmdrO9}PGok^$;kI5H)?&^B%<-M3k5^OoQj$Ane$ugy-py`EMe@`lL0J2Dv} z90m1STp0;pwV$5<6#g%Pqm-zKGkc0`vOm-(pixU7H;*4PK9P5@qf7Knaocn(&0QOJ zS?i+;=h(j!M%d+M%*Olx$hTUk6fWnz`8)1C1~j(XIT!jo{#*3QrD};uVeQp4cI*~sQZgOV9GG% z&#pIwomui$7(G9=zj`h|Y<$DjJNL)%de20<9P9gR$TMo`;jt7p&LXT2k==NZpSG`` z7$`Kw*a5te*3OArke`T!Z)A)zZ(3f@V?mFEI#O0u9i}qn#Y8}N6lV?bBZYalb7D~S z7hQv3^*n@Y;9-7$vua3*F9{tWam6!Ay|R(ZSmM#mpb zzk?3ECWj=|ePm@}+Nf@t4&1>zp-?cg!y;wIvyy2CPzh8ykS)C^;NPZ9hH?R*S zQo;|QLXd+#yjx_a!TpD>BQlTYlO}Z3i&yDNi6K|^7`>XOy6anNv+eQtw5K;=!=uF?~)pdv+!oi@kAkQEU|1MM1JwZ zO5#lUjj4))q_?86l1JB}0^=~DD>1%>$6K*@)}r^ilKNtEKu)r^r?>5g*c`BCp`ed3V3A=oPmYVI0y>1 za`d88bOqW4=P{V`^X>DmTOn7X!<$U)a9uo4yS-h&3`mj!YP-^GPrwFOOzks~K+Co# zMa-s}5pzx82CY9cMd3nJN9tjyaTcZ4bZiRFFMo)Zq3 z)tHMeBQ)iv10*I+$f}qoIhNQ0aJe@_vQL}VAyeZz^cKJTgy4xXEHDdGj8Vnk>vn1_ zCXd=VbPks=RR7ap4vwDPD98MS57xK}qI= z-;AgX9T2atg@k$E{p~684k$9sg0b=t@lFd7r^f^bAN)RK*`1uz?ZJ477AJobg+rDT zqv1qG2`%bM6PA-0zwSbHLe&jUTm0ftR%ZoQoB6+eGE1)QPb-avZ@2mJW2vq8k!HCd zeV9XFJ&V^>+8X;Nt1OlJ47AnVq|?sS#}2biG2M^O$(9yem+yiH0x<;5G%>p$w1Gzx z8nnCN25mIaN$3+e5dmquQD*(0Q^iJGF;^p*+ZKRW7@?DuD#HnM%eyBcb$@43hPPsc zPVb2gyx?$cb|AtTl0`@oTb&*sajZnsx;84EK0?A+ijg7;OE%cKNhS#jreud@;~d)0 z`10#{jx;Kn^;@Z6Q<@eD9az4w?W$iYYDrPU1{@U1 zr;fH{kAPPaFLFdjW%Ji|2W}=s9e0RVnq4g}s^_|sppQ0SnvM)h_!xesY- zn)iY9cR#=hZ$S6HmDd?TP6R)EyP0qQmj?bTDHv}lKMN}j9?&dvO$ zic*P`W$f|yw^b=)2`5e*xc(HIqCbU%a!zm_&U*++M^KbjllW3ka?YXSMH@$qkA6Sj zVw}DGv0!qhRg+T-P9HB2@n!?VSIz77X)Vb++oOyd8@lwfH5LaO&VIPXrrrG}mY`uM z)prjj>YxAYZ-MYCFGJAJ&uIg$sHjMqG@cfq@_y8;$<>3r`xQaQ;XK2P3#J}Or4;nV z51$Yy;B~#P$g=|*7+T46li|8b>vFnm-UCZ2fYSAo z#oqqUp|Z!u0@S(7-<;1@RsF$gug2dV(c7d|k^%ve6!wgVc-#6?&`y4>T&Z|FcwL z2aRIM-@YOJT6z`i$A@v>Q|@4B`Y#us)9pGqO@Vz#c&A+gV+O5_JeT=Qfi$Q;}z6JHx|L+tF znf-)S10&Mk=uM2_s%nw9zlg?~b|FCa3?#wRdxNwt0RNYN`gb7P2@Ctv&{tqOm#9QW z9DdJfDOSgkREb>viC6WZT|E7l6IKe8|4I25X}(N3M~Z}$-7?_dMgw_9TU-0nij>eP4CEON zt8r@3v=H>ijy&U?Z-tk`ITVTW6sh?x11IY4W##oZqBRE6ifJ2UJ=Qwrku9prY@?wQ z@$r(tqsKgP}0z#L#Z1Zlg~ZZ3|kkpZ*R0s#YX|u zo3fm1`%&UTOyNb+oCH`T-|A3O|ky(x8%5l_Qs@ zRP0?|2w`T7in6dVbr1J`HFb{W7=vvl>z>R+Fk?xx&<{gRjWfk^+(obGE4RghUG2)k zmK_!_DRyf|k^=lWK4nFDrZjKhujzyT_4e+wfk(Y0OD*wLN1ZW{+G6^lggB7AeL7W3(EULDZt!%0WqqwUGf* zmg{K+S0Ezshmu=@obF_?@)hb!J{1CY;D@21p`Iq^qbQLGR^fQ6%g9~*A9|im{mh;w za($p75eK+Y}nE`{@VvW_Nt;?L(3gH1=B9!fH(l*+`Vap?drJn$o z91V2M-U`8!uVkbN@dQ6A7BgLkZbyhy>P;nJfq|iip`3#nWke1+b$o}Wk~e-}cipO- zZ{Mc5-aTcYu|P%U2Y;Irq`f49NAu;%)+GR4#z-qJagwVf)P+x79;5yqx3tCiRUu%E zh!8JGcPdDJm9pzi)$uvLlA#<^1%l;Pb7qI0q_}@FAFoBRGnUIO{msO9hO?L8)M#)M zvJ#SoYYoC6^fARPjfv3vWhAW+Cb-C08|SQunu{`jbV^Jp$6k4gA$SQN(B=5h@ZFne zr{2X)g{(A>x(2bW`Sm)IKtiMB+Z)Y2*|!aiiI z{YptHzPxl`Dy>1iRyUbepfV+_S z5@+L?FYd?sjiI7iE?tR}8Fk^yq+QjhEn)rje1kO~hIj*s;)MXE)sl<4d2XJsk7yc> zyp_4zbWbgi_wL7n%QS9%Y38aPaVX=MdSh|-x$9Eq_X#6+-4%qfu*y;I=@T!(QtVz! zYO#|6mAH9sn#2TGiK{jN@7$XRWVi41S)CoF>6_Q6&mFID7!w!x+rCYi3iSoAf2K2O zQS9X9V`z%hiEzW^k$Qx|@;!%go@he!4#k?mr4R4E@{N{gTHepb$`aZqC9o4x9O1Fc zO<&ETDGXRD`cm(E`=7#4qY6=9rb&v;c9=Xg)1=N1bWb@?=dfGASP+eG??hBbx&i|_ zjIyeyK}s)$Fqxwe96c9GzZ&EC^T`%&y{Vm4sDSVgG;xt7)}CI+_k?fkv6&AiA+u?B zgg(5cN%i4Udw%3b5vebejoygNxJTtH&8Wh1iK}4G%`tBm;f%ZmzKfv%i7ml-8$Pr9 z#mV}sN;Q=g%0y+S@W(-tPZ*JNF~gM+O|v21EngkXmN23FgudL$5prINI6BrS+OfP$ z&deAs44_zz>%|od2!Dar(!j%#8EQ6QmpzRFxl%OP51dcC8|!#0S6YnC2wI}<_TG*7 z*hHKC5CM{fA{UPBhf{98@)gYdOwA;xY?Bv;N$q}h$T)1nswyoVY;41RY)y0>1>T+Y z3^f511-R)WKvX+E`bUjwIpf9=AuWj}N5+77*E>cs;Fh(K4a!MiC25cwS36`p7I>7$Qwx{mr+)B9P}s6oacS0stg zmilQGtv#4hbU8&LwtP)bOGaUM$ZS-LQ&RKK z8u~Acl~8#CcSG@~)7%`E)L5C^&E{(TuQ;x<;nk;TRdx$uOQR71zd3U|Oal;(#jH)? z1exSf5UJc6!HE1IHsZ!Q>W{R5v67Q_GG7Dbe$HxQ^qjO)w2x(1n=YJ}5D>?MLaK_m zUsdsD`olL8kC@rS*j8$77vH(cg_UPg$tsC7?F(@Gw+z^lL`GO-#hE5>@S*qnaEV!t z4NPx3PqVf!6XM$%|K*`@IKs-UeA5BelYsNXIq{?&6rUnUz4I0o!RAEzf}sPK1FhOm z61zI*gU0#M6MOopD8tN|Djn8rhtGFqzZ-9>$-R;u)mR0~TxA9}@v9!EWoL7bIF;t7 z8jxhB6Pe&nvadyZqxTnO^wBQ>o5@S%V98<&w5J0cd1oRx0c=0DmN= z@87?Npe<%@e2fz)J7?&K3*l53oh~9-)4%7LtiH)!&WW9msEN#>(5Y@Lcvix$a5 zVyeWi`~5a^RHR|)guBh*H1Z5d61%PKiia}%n25_EU|=q%OB{XVO5`Jt<18Cq6R`=W zVruSQv!N=$LPywFfaa_H>_8zG^4SE28o;ErVtoHse>OxwqD_(aHj}=%9}?AGP2%Q1 zQGth^IQzQv=`Ff}5AH3ijst$JD!Xb6mm@WUM=+r%3z184Cj>g_$L68bfWGU*JPt5? z^bt8;emjY{^%gWZ*G2gzrsss2QJ3+OkT1$Dt^Du8{8EL%_Pstu*Tq4)uRY7zba-Ep z+&_{@v4<@XdLlx8vq3MD;jZ6rom-n&Nct5q*7}(SDCk7qP*1Yp2H+vep3^cia##)C zqB6VO)^^83sj}Bbb*bg7a%^oVw0s#B4;He{iTr@+&K&+s1)fIJPJ%oVB(k#JOE>%)HVc@w#K6 zhKZ-TGLQs&nr+WBe~UDaaR!u4F8w-Iw*Wx z+;RM8JSjl^Iak=+z!;NxIzA9#i1%^xcHU;iqNj)~EedJ1&r+T!U?0K0PVc9VdSFAN zDz!x@bBuu1iV>i4?* zm!^^CA^EU8o`e32%~9iqX}`KS+Bd%{=Nzk*&&xi4EC<4BPIl=Hzmi|02*|Ow4%2C( zwu{X~%Z`v)#wGD`VDt4B%Xx~wEJ^<*BS?=%2|aPrG~h*HoJl$VTtKP+2*U`Q4Ea`3 zDbS5pSAbm(*H~T7qmhh3uw5q)DUH&#mvM08`wF4PZ8K<&_G7_1I*S%Tf$J!hDZ7t~ zQ7@d}lR~1*C~*Emka?3G`JooG8f!+Hk-5jX$6AIdvc+_~YwE zvUACob3Lcim>)%g3Lc7Sw74i!8%Wdf*Y|yl%q+FItqMRt@z)W#oP^PJD#@28;D{G8 zH$wh7C9Ue>VVZm4)6^bl*mQMH_}>X%eJ^l6l{$9iNzJaS zocex2d37GGu2IOg;qOBUT>Fq0424a)&t(h=YX7z3#0+&}86oR{XxVU=Zq=WBK04Bp zO*KUnK+N5A5g{-Ve9DIk;M{`M^&4f+QbGrCf>#_|)E%`}RbtM5-4bezeR559ew4CzV0L5WLUkcE9{%pOGz^#DZEP zkm6n_kYUHzF`Oo0J!qN5`HW-wi<3aCDHKg8!ySkb(Cv27R6KuKNkPC^i+Mk2B~zAPS?I)$ zxnp!Ppv8T}A#IJ-`aq#q6zf2Ebm3Yx;VwIb|Fzi%nnRB*hRMa9gE0j7=uZ!@abWHk zwwxpMnR=CdDd+QAO?f886)j+neUOx>f9F{V#mw&HITb3jzwCXtqRcjZdfK>!zSSg5 zHF7K41Bb8mp44NX{9=S{JnX+YJdZ~eT$_v^3VoI~g#8qgHES%6gu5}mZhXb5y$1U5 z4j0e&_{>MEqu7m}8_6EmuR8rD4_P!h-}kRYVL6R^1pI2hf`+JSfBX5^)yOSPA7v%6 zmc_Iy)JEa)93cZ=*DLPOc!>nr!(-(=wu$EwD+k9%P0HitH!{)f*_thJ17`N4d8j8q zN5E#d*g6zd$WBGwacaBlG!gU|PCzMJb&^m;JmevazmqHUM~=j61= zbP+egu%gC2h}`lO??rEPb*rl7RKS^+=`0Tl+jGDEPq4dMQB3uYcLi<ZfL7_ zCZ6AZ9&Ei%CAAr)Q;&QF*LK%IRhVU*!=`FxKydFvQ#i2_8?U8v)%{H_mlsx1VzyCr zo?rIn*$26IBH(njZ;-l^mN!-hbNJ|jz9M?~X(;@^g{MExP+k+@Wj%GiGJCA~aWd~;AHiaS_>o99_rISM+5wOejs^pMZaSEdhcmJd_~5-`G4+BB~)6_VrGKS%SG%A zarv^yAxi$Ex#SXTqj`Va!r6|ZeRht%xcFM(hAFNNAE$B9!9E3Ip_B=-hjwbYvYp34 zVd5H+4&zl5xPw-)!CW_;5ibx*_xjrK`XSnJ?fC$sx2G9Wl{b8y_z%M$LWmBEN{ude zYrgIFZIvQ7)9825EiOZG`5BT1?c?QU(nNd8brF3xX=0;=SMEi?Z&k8f*B*P>e-|jU zBQ~93ASo$tMKmNU$7m&cx!8;OFe|#Icj(rMHavXF zR(vm+nFURc0Ys%ewgzNxkpY7;+@hoDX#?VlF5dfSeLP)J|3nH1mLAUnivM}cUIT-G z4%|bgf4c=#9Y|)Wd~^+01{*Qy`Aps85la^Yam`Jj^Ld^Ei)iu~rKC(#L^HaHtE9Ua z!h_1q^dXLrERri`qI{u3(oU4Ilr=U_@s(qLZldiwt53(!|K5hiB7o9eo3ER6ZQBmr zmX^L)1GV>kAUaB=bn=Px!*zj>+-Tec7(!y>8pAeui;Oycpxio5c57ZMq6V@=P|YOq z5kP&b=3Mo&6XCoqo^$+BGz)#96g1r*o!@&fH$Y`$d+&%+um+?q&9To=(*T*=2j;~1 zFPurO6?$zHr3nB{Lx&CoLbNMqAz9*zWit=Hv^nc_-gm(8;GrwzKP}?0KS6#0T6;-M z?U`JGuq*rAohs9J9LEKy+b*JQ!zz5iN1J&`I>=7(9J301S}5m3X2NanM$;DP7;Y)I z9IJ(tzd4(v>9x1G_OKE&6zNb#EIsbb(WRem#X~klAFsFTC{V&KgHw%_A4N$JZ^SjL&+n2xSmmH@$|+<-E4U^Tu+r)e|4zh7 zm}GK->goZ9rYCL^Adkeg=Ec41HR8YY(?Ag+*UJnh^D2L8&g&#D~`EBDQ+n%6x@{ z+o*w-{0G~}e+`9_`9<4rE;jK!Sv_gY@ZBX6VzoI&_`j+-%|H|* z1|Ru8Q*Av9Oa8}ANHKQ;ye#htng$_Y{d;5?L|RG{q_~-fb<*VGkV?>B!+7-mxlS5e=1 z*s&UzgSnctzk#Tb=h1)y2UD5pbt(NI{wM3z z>m`s@1^pEv>tV!nyPs&E$LU&7FDfX1L%3%`fk+DiSk_{WA1rNKc*@IZ)>46}8?H7t ztTnN)NssKTC}Fm8yytDI9Gn}B8+AeX`;q>?+E-k3yyRYYl4F$DNI_ZS@tlfO;*D}R zlDf**Ud$vQ^9A9x!?!X5DD8s7o==|PvwPC_zast%y`o(ueoXj5tJW|~vNh?9~QH9TBn#H)z! z+LIb~o?4lzwoA4P5C{0kIMOZkiDUgCn7f>F3|HcRJasm+@Ps$o$z{vuB2t$deZT;q z<1XRn$=SS=sl>-jJgn+0VQEiNS<_?Uk0^zJ2r;D&-5d} z9-fwPsSRI+(=`$|InxL$Ux|bFqXm`8wuL%7!}NBoQ)KD|LH8Px`c>VzBwE8CPwx6y z{N&|1orh+-6}=H5gwfN5%)zruNVrCG8p?O+|5r0PG&P^7jf$+DSXNk6Nb;&&yt?Qo zD^Sl&=#Ef%bmy{O`tTHUvYdmv9+I&aK^#^|5_gS)_<=L;6#cSXs}uNQp0ENdQvyv zm+|zlmsOy+2~kT=eYl!t`DCIOj^glpp_5Aebr|ssEMu3G#Y8F53g@`ZuT4a(T>B}& zm^LfB`8{71g+>~)Xnd1+@|Onau99}AgdE(Bk+bJ|z~5dO^h=%w-^jQ--d;mThZ4{~ zM^_I23=(?LS$=*-5W9iFC+cVT@IS6JnCZG~53%f@m5~wIFUi5UlUaMo1a9ivO!R{9suiDE35LEi|ugC2Vh?2%^o;xc4`1!z~ zLfbFFw9UUsuR7O)E`k56GL^mc0)bW~YWQgHN_4?=r!5tpD@SHH-Hc1Y++U~Zh!@E( z5gLZp!aqkReG_D{xp#+ymI2cgT8k_=%w0!3-e3wLE*9c^_4Ajz1|o0H%URoztRW+s z%-%VN2iS!V(njHHrE2n048%4`}`<8bEsW{GTX{f%Hwe}fa zJlQWQcYg}240(KqWny7%R-F|JmQz^I7je*p9hw zyD6sI#EO1vzW3c^X6!O|dCcB}jgXEfUSRrThux$53cK){$5GjTDZY$y-Z2N zg3hcubG$TH4Mr9XaeEOt7k?YQ|5pwv9=_t;dPf{%q)P*TjN196o0doF}QHyLMM{ZcYtfB(~=k}l&>R@?h7w~@%a z|AF@zF%MHc$UV=&Y3aNsmzU0uLy5?sUpOlAy%V1O%8#0Sg-p@8a#d%-jcgXW=fhN- zKLBsgjBeX|v!`BxZu_SJ(4n=SQ@6MH+7i~%v`Ro_szcVUeszW?d>3#to_Cb2gl1}fc z;)&yG=^n5ZU-W2s-}`g`aSk4A!GA6~Dk=uX@NN&Y*9MA%{DD6VLKL?RkK1{++h#cR zpYv77dlQ$-?D7*xX#zrzld%!DURy*z-^$slHJiJOO{Z~zJK)}TE$7+qo1$coKaO4A zNV8Gr*`ojSNBp%b*$%ha8wK&xLiCB*A*P}}{n=_&P5tKzi9=Op(eF4cBU$(^=YNOr zGABx5pt-`tDDBa7ex{4$);lO>N|89M=+M&h_BFPwz(N=up9RGN-OH3dwa%YU|Ib+n zVGAx~qM*FBLU`)|{_1gDwUjZ%j1U|MIaMtKl0QeP&B|gQsZqvMam%UH=p^!uygbn9-{lR`VSMrEM{SQ7MTq8~3%6qLeuD|$hU7T=vTlB0C(qlvJ0ds; z%)-C-?ZJ#B%hbbgLo{TqAQ(h44f-k@bU#GS&&a%2`nah z+W**&oWpSNjdNV86nISox-mLjniz+fve=NF-yqc{0T2ijE#ilSQAq}J-1vNQ5M&7` z*Wfb4Ip%eS5M+Fi=|0Hg!x}4USM4-wOF<`6!k$S_P-w9iq!%WmFPZ z5HW@JgeF7+!M}|M+ob=2CXWk1HAO{(G!Rr8>ZwWqAN^kxw|JL>0%KbD-PZ_{uPCp1 zqXHp?A4DSTY7WR$?1YAoFUdW!hE+5}ah+m;5G1nk*v zdV9AjBCSQa?Gu9p%4^=MDy zY|lwKDY($_+nhplU%t&)bp=*35+q~h?eEngscIq~SI?UEBn2_sp2WACzZ6-^dRceP z);rnd>PvPg#dRTN1pHYqqTlGT8A1lJr7tW_qbopb%`f6JF5HQU--j(X>CgT~L2b{& z%wUHi4|%WTE2I`e=)o@VF)&^t85h(1upo=R!H|Y7yE&=_rk4mE=;NYXaln3z==NTP zn|77MV&UC+M~2B2LjOlJIhH@oehgPkrdw;5yV<_QQlTKwGYP8xAqk;Xuba+ zdfG&;C`gFAb$T3Wzj&U|!YLLXvbaIhHkoRRp3f%kJM9JgF%5m_x!}bmL3}ff9``|e zRAOTcHmfC(w>$B2k%5^D88Sag`1OmR{%vqKHTA7KQQqy{dU`ssB6q#0@-esSw!#VS zj%E(W#$fBEYmW8kX?zYWxXbi5Z$0~rZ4|Uw*!U}UR7~7nqSPu4|5J(Epp|MpT3Em; ztGka@XbAOExfpAay;TbxE7ht^V@XVFE8 z{Py=MnMSK!HzAjYx2fV8+nNYn=lvH`jA3w|;)ee{!wjQX15Sy~Vvu1ozXn?$3gEWv zQYPXo1SZ5chtW#>q(F&f#sue5*3Q4R5X~9#wq~>lWKav4pOS;vT0T@4;AX-B6Jkb& zL`bEuV&<%lZZq(&97PqC8`EO6K0xYl4#aU0*sf+Tx%Q0;r3E&;Xtq~ASG$@xg*?Rz z#$?tjW~?32x!kO+fl;hD%~&90B+O62MVxT^$x3Q{vSuT@gx<%Qmk%<~=qMAQl}4&8 zg_9IlRs4I^xIA|v#5}f!#^I?DUaU=>0}cCI1Gw!PJtSyws6cy277HFDSTNd-9-21_ zn^D7<`)$X_O}u?q53H{Vu$0?)|1SLP+X(^s@>2ZI1gOA>P6EZsm(Ni{f3Y1j8(yEe zeH%)79W)Zwk3JW&h9o?nD$d`1oe{!%$_Pwl9>n;SHHb+N_4=$wF;uZ^jdL$+N7HT* ztItNw%gY-D9e{*hiBXq=4WW2!&1UjSqu8m7=P8~ijgVsXrlE&dTkl0g{N0jhBzi9! z?7bDD2S@Oy!O3muyn(`WZ1`xA0(kML!{(KUKP!WE}>Q1iz z!7@S6VY+_$eW%^FrFOlule}>Tw-B^6h-Qp`#)9nA=c|WrLC;lNS4RK`wHjjcEbB#&Oef)C=PS>iYk2)7zT~7P#eY9)MvH(_%4Gf+L5_P zK3#Sm;c@I7{ zj`CE1UkF(0M^W4Tfef`p<}xhb1UI}n2_1NWQ8A434ZhH$t1QKIaqkvWf(>g4EEoKU~mD{~lKcTaKabX;I+ILoJJ_1Vkl*Y?r(s@{Rz zIzH)63GaM%kQ=w(-VUpdtS1oZFd|#LwyOE^6MwE;+Y*OZ_Ex&C=o^Ho0e?_!ssZ~C zp)%Gs8o5(nvGJjDr{3zRs&GA>`p)t;i}?^;$BbayCXKI_ht2@Arw8H_cn_^x6z!(6 z2{B`0y-s}&k#;@9N2DBMaSZM&hKd`ID(2>icFmoC2x<69)W6==_7$d1d7;p zzG%pMmPJa3Vg0J=!j4M0PwTG%dp{phE`_w;D8;^05KbZ@3k@9;R`1c>7)l__FHiaK z;?^q>E!TY!Mc;c0${1|rg#Z!#T6hh1Gs+fInpFqoRiV}bWg3x8{Jc2~;m$h~_Y5=N z&ux2iR9poU=w4X~^SHMN+n)ORzxCsl^*kRXn@%9?Klq*5x*R)BI@B7;ci-yHys$bu za`(@w&8#b52FxK+hC!jw!jlsTG#$L-Jwm<~t_)>6j?~-WDNm`CLI`Vk9_S*40Jl;i zz5~W5fbKLx-*)vejn|KK@(YHBM?i>%4jv>Pu=Sb^TcRNOFUp|`0DiXYMA4qxL*m1X ziZD1f0(mCPaMMY7hO~)>t`9%5i!m(z{(dJTH{{+pHRrcEZOaVV{sH}>3lo+O8#w%Q zoR-*K?n)>FXB-_!@g|b1PMEjoZIX^l<@mn62yF@p#MW%om1&Nz_fjqao=1rGjrIe0ETd13ntZVXsUqHXAXqjtE`Pibc#XG40# z^z`tE3J8%VL7k8`CHoDbpQTVv-;N|h1-7*CD{{1~Vq!Jm*rwqd&QOh38h-nMTgW)g zjeISm1o&RKYZS(qT(OaQF|jQ(9_d8xZ6;BmpOS51B)TNA=KisuRw_ZeX&Mug%6*7A zO$KCUTCu)p1^FKe$i!6cKeBuTvaH6hKn)1VnRa=#q;9H0*9VQN(#HI`B3i zv*$7VUy7ym?B$wv+FEPYgj3Sirde?n>eO743B>7~K|vSt7pL~BS@W$DY^NQ*C*f6Ky zK+~k{ClEe9J>4OpnA~eJ&5GAooHOn!n$A{A42rsgEYn0eNLwL{-I)6@1j!127ZCG2IVD7qx?D|U_Mxo*qg#B+d?jB&Ium-1w;GI#Y4W_7u z@UqVM33h_|x|U)~*FIYCKEcelr{Os`ioGns^GqNf5i0p>K;A)!{Qo$6%c#1VrcL-9 z+}#PT!QI^wT!Op1I|Mm61P$))?hy0fwq_}~+9TK+*w12zBJVhL! z%HOJ>-(I4Zev(IEL+pybyK;&>O-uEfX(8U5?oT)N2KG<%8@r7QI0cD328LE7O(P@L z+}zwBdVzDvM49q5DQe}!hYI%|RNYh?E0%91RtukS^})6PyGwQy20GU>-ODZL|M90( zF@nFfwH&}ZBA&WfrB1$`zzakG(gXZfO#I@2rIce)bN5kt^WZo%f09%O56c)LlE8lnP)T1Luk5!wj(Z;j(FTu3|U2`bw7bE2g z#R#_m<;lB07hi$&3eR(QvY3{tmKN^JP%A4dg>DM{M$9OxB-s-6k$#*?I0VihfM3Lp ztv}t!^@IE-wz!(zcoN0$&WhiiOEUcBxO4?=7;Okcu|1sf+Mcn*!%pE~>(4SPN1d zU9k{wSdsNEEDSt9H%Dz|nL<^I6fjQVHzB`qcy109I<^4cX^)xv`@s3lO%8zL0*{IQ z@{4yxSooDYOqg&LXDd7$t|j#E(-(eN8%F(8mDaQbqOJ^l3a;!IOG4Yp=9d~Ye0&1qjDJ*{2y;U-ltiG z^rJWoKK`C`xj>|?kwfBFBg3k!th5Z^781(7=F?D8Rvw)e{KmFZBo?5x;XZ=-+YcaZ zfU7fo1oiMXOAV}5!R0${GFV<6!CI`oO2A9gV9a8tNx;!T5B?5Ier+`A{jol9&s1Mm z$NQb7A1@jXVY>V^m1AaiC|M&kOXuVXn)4D2|gHuq$$yA7HoBqu9NihtFHyqWF=s@!-jXmH>X z!sY)gHW;)i+yhmEe@@rC$j?dbxFDDTF}A8?3OU)?DIW94C-exesIuVF``5FgXJT|J z$Q@cRNdO%ozZeg$k?^|QUfA*e<-nIg70ByZ>ri~~6$L1CTHwrsOVCaK{CO~FeN7Fr z_}&kwjk6t=p$4imxPM{$0n9z4k6bJSAvl&~X4}_AI{Yn1(;U*z z1W32hmaw!hcu(z=1zh5s!!?&LjYUW%c9z(3RL$Sm`03OktarMkg}7EZ*GS7V3pwc1 zo-7nYC#gPyw@KO&hcql!@yuCI5QNeKvJyb+;(64oc2e)FH=fxzcH3&Hb}k>G%e8lu zD`S&`l!XVYGB=c}bUT~_=*>^XwR?6^8C=Igf7*_OP`M(}u)hPGN;ZaEQV7_E?_!Wu zOyK!V6PD%DgiX;jl=Wm{WHg#knM5Rki6Qk-`VpLD_6_;c-(rD9JDd(IDj6&q3ttKtIPRwnp<%TaWsLVLW2G%!K z6DbSnk)>fg5_U%&*478R3SiJuWCz@!MN;iWN;E98#+kB6cvS<}*`{Q?(Gm5LbK82f zIco$|>|zl~${;cj>izzPLh=bKP*0 zWabVZj~L)oA+EF7Ez%ccj`MsfB}hkGn7PpmGP6c~{fq7dc(nludoC;vZEbP2H4KK^ zD*X+;hvbMjjs!CY$E}W~sz?Op*>Ex5#VHm1>K*17?Ea@D6cicf=d_9=C$H0y`1tDr zhTE^?-`9hx=+sXUgvbN-md8fZFLWR>``3!Xzj%nRuYQ2>*L<^p+O>IL> ztE%Z9DHtQ(A2a5@tsHNn--B9~jB@yq01zk%x=E=+w5IZe_*XAr_{ z2ML)LDF3SY-TI-P z%B>w~)>4IA@;ED_e=E{{Rlo0>fG4w!a+HxfO^!b7R)k+Z9rG6V3H~yR#ei@-s2Aef zvFmNc1kL#@THuZ9-Z~4(@{B00QbpdEL1bS8kTL%j?jC^Tj>JXBK3yc zD{sjH?FC!EZ;o8Jj))`M`#FEwKWg!T98GNlAYJ~X4 zuMb>lw*c1uLBT))m;aA*Hp{4!7m~(Seuh6;)EPaI(j#e14sq_k`sB(Xyn{V}+H44d zn|8UJE-1b4J#bXL_lCz{DP7U1z6&!&)DPBT*EGYDsHvQ>fgwBlgz%Ff1{>){Awazm zKbBy36LY~CR>)qp)%A55*|23i8@)9QW^W=nM1a>3!h^;lSk~lWc4$2PNttYz19~_k z$_mgu)QG{mPGa)AC{aa}RH>@E4SUn_*OKj4NUW@5c1Jtjj|Xh@-EyrC5rdcWq^@d2 zeGim%j{dMzGrSD#-6Iq;cf60+1MB`#XMR!ebm(Dv)!ZXKSbM6UsG(ae1303orm9VM z=D>B^*R|oA^68%gFl2%JJqGxtnR&@<%#yK53dg+H#6L+pG|0xW_zl-j1IO-B0svK~ z#9BW<7Q+fa5#GB66o)ul+J(>V=eQ~1eV{Ql%i(el(W!ef=Yy4DfB7l;xj9UP@=kVy zH1wA|B<;>J*(5YRP~$|wsrJqTS_iVi8_e)OE;ds@&D~VVJa}|0PW*}{DP-DTKKJ(e z-TeXqnizk>$f5F^e?&ZQEq9c-Re2%KB$fDTw=0jhO&f}y*F%Kn-}J3eAyH-Z>(5ZS zdE#I#Rax3_7UaVCKpVsZ+BG>9b=$rMx3aB4F5TGc`l_&-)mR+7dffhy zHb$<&OxD<@vyd2Jk-7ekEr->**ie{=6bGM{{0&e_oHzJCGxQ$>IAIM z3lH_U$g@N}MZZF$U>~*d1qws!gm)4otxX{FO(jp7^}60IvTiaZ(`k+(X@uE}v5cyZ zY5;T1$8Qd*dVM|%!`$buI@Q?ac8{X zn@3$$J%FDF8V|o2IjeeP=s|6n8MK2JcYvXATfsn11mOsVUMNwLLDZ%K5zyc#Nr~ zJ1z&Hg+N7{qnDIe$>s-hFf-%S={E4V0-O=i7U3WykJaE9VSHc%Ci{w%kn8o5iXpXUCdu{{B zK}0s!>Ae!U$Ot{S*tN9-#$j6mml(0E`A0bu074bso!9FA^S{XOZ43M&A~s+J8UHZM z=XNcSmrB~`X*4$A#)bxdyzyso&`Is7V6si+^iUFSdHjDQj1SXZNmf&Yd+T=?;z&u@#zt&foQS->nr6ar!*#Lwo<9#sJ-`bx6mfFitSQqm}5e4zTE z#UD5nTjomyuf?Xt20vWKjZ?l=5xbb;{T^@jSSmLAoR;8^9#v9Oq95{11F9f2!lm*h zOhBt&B@(D^tJRAS?%yA$3Kdg>(3JBf;R8o_{Lm}CP(nYfXV#BwcX*fb+#D2jxP@8!8Ap`u2<`7^K`O`g#0%j^;S^TWQJo?aGiDhyN-yWNVgTxm4nnE{}W+%Lj@CtoZA*aJ<`6mld1-q}_k@S#6I^Ex^% zan0><)_?O3U@4-LkRpbN{7n?xw2x#>2_ZZKDEPi=SH%DLG6!$#{rmUb(k8l6=`i#NEW$0nz8wmWaf|k@ zcfG+6zUQCRLl%U(;4Fh+I(ilK(205x3YOH1JnJKn|^Hk6Ry<8*RNnmZ%TBHM9)Plc4%^9 z1Y44=DJ(PUX5gm3h0Fb`&kE_j#(3z{MG^6Hu87cY<)WvkwT`a`J~n0Q0OhU%$p}{h zzUct|i6Mme0`5gmb1|nr_DCri%L%(%K)eUrsOsOwJRuxOACrWc%54C10J1M5g{p_J zu&~C4y)_YV6k-X)$IFr>$l@c`Dl+U#KIyVEmjVX}dsE$Qd!;T%f${!gVKR?7gaMcJ zPJqPoWu4ax$&iiT{)CMS?<(Jtoz0a1uI67-vJHF!)cg%nfFhNh-k3|7_^`ORIQ?nV z4mL8ZxmPujU`yE%pN&|jQlV_#dO3vYaX#byRAr7h)XyC&rsNkZ^0>5#d(MGua@wPJ z2d^YqoND=!WQs}=H_(3FUmaWBgp*Z0rAkRFhl^kbuS#F^p|u?)hwhMnV;(VQg^56N z#S&veynHBoiJm`)qJ2ShUphdX%K*XDI~~MU6V;0((VwxS(k>~{$Mkr5f*#tr)W*6U z@}I{gpFBvZI+S;-;%V;{4!Na@j-6pQwGmJ5ml1Whu88`?Kb;ysj0thd_n&N4ge`y3 z3GIW$r+$8uU>+m2)1@=mk7vghVZ?94i!ma!ZNrl41U9W#)~J=3c`oNtQB@_~298E-q%ePl&sn7qU{+NdGP<)u@1d-9 zdoE}Sj&--~HKcT{+t@}2o#DYt-1fLq)4Hd#)6=8hx9@8Oo;thwr@IA!Pb6(0o8qA% zTjO~x&H49YLALd8}UQ{yddJU%vNOUMzDxK9d5^&&Dyz8sP^ zm0DyM&!TE_8^&DmAbgro778U))(-SRk#a)>%;*iXUOQynD;NtnuOvMX+3=KU%|+@x!y&8 z7*!-7JQBHerp0Wd%^+)DH|#W3?l^MOQ(eRH-HoXXygiI}+X#6$UBC|RZ{~m0rV=pZ zJqQ4V-FU9sAu=bwbYQI(E)mE_;g>=&7eNkDl2S=;{^0J;YL8?VrLJ|%QlQLZfAD{0G* zEwH{-x$j4eGs_J|R+eF!YraMN2vzDuNu#4IdY``m8oL)Wug|pckwgpbAtL0iaw<_b zOcWdYBVr<3w3x6<>(Kojpo}v=F{~wWBQ(3ZE0xs0!e8G7h`$nyfbyClIOi}^a+yUQ zOg|Yje)P##3%}&PqiMepxaV-lmWhkOBdcSyWRt8}&K^ULGSXZc@_s>xeJpFlk8cJ5 z{GPd)?~wb9k1^f+W!YY+F6hD+s}+b48o|&xh1EK=JF<}!bnC2A6v(hU0(~T4>L;*G zjFIf(h48~rU@=c?!`C1x?fAJh6w$dT{06)t`uFi-mlyLg!caz?KL;=@uO*)4vmO9f*#6azknTeL)KaS!N$hsvXrSU+S}7 zKH1m1%8wAZqAb9UP$b(&CgrpkFe@v zrl3`GK&vTkZf=+m2*DY?hRcCbM^zMIMCK&OR6Tk*waJ##1U<3zqGUw;az0ohday(g zJjSEUv^swYTwN#{3ev|jpfsqf9#OX210%QPd-zo&As&pEi*ptPMEV~SXd*p?o%{~_fwg=Y%6{U zuE<*eluJu-O`x)S5=Hh>tB2Qn%EW(&6&FR%`f@ulyW6B1Ps7qZ0L2D z^yt8L1E5I@v=hl?T*}uVGXD$Ruz=MkfR%Nz5%1q#NgK9*%d;R+2+M7CtleyPNW=Y! zq~bPwj^l(f_*-28qS>O+W5tiiL;5^iA@GSB5ZC|t`!x&+cnCn5^mS5y0LY4_|?J{qAbRs)adcgimrKl7+UFkBektnd^lD4dcR8i}1uB#A}rp;~rbT_uWz2p$o9z@*WpB}Sz=NpuUIc{*V=>RXdoc-3ho z^b^NA0C$>hSG<9HF1!XpD!_?>2To3x-7}1gF;Wk+FkGLJjJ=^u;q{JxU8dHSJmfv) zm6kbwCj;R>)BpLPYMoeESg1b;@M6R%sKA@BasP>pX6-|1CY#v3cJed*XHj{Mf2QeZ zTN>K9v2fOW^~{XFGg{xuV8hs5LGh1l&o9h8LRE?58(kST_b?KIZ3o4DIaH< zz@e{q8=i6%z?|QIormFAV@xr1BTs_R-_S6PqW)Zv2}GZO)$3)MZkK19Bc;aT`%Bz; zU33qC;8&AX*Fqm0U&%`G|LHTBrb2EJ*9Uj0>aV}Sy9G4CV5lR-k;=@*|CW?!2^q7w zBhNNcTHo>T6-&4-zd0umVaYc`6&nzi8d*dHoJb!6tPa`jps+WSDwbOW5;P(_JhWA` zCCl-nyxQM5iI!fE$6$Nop*G(K;t@W1Kj&xvLx{-el8~4<8aam*$>dn6hQtKe&vbD& zX%X|EKE;bepIA)Hru9{T@>d`lk;f_F^LnH}_=Ox!&xU9 zYNd<`hTNTBVnQnm#hJjmW^1x91ig~qJkM%2D5Zw%3SobLzru?#%0F*Q)Y1-L9w=)a z4;qT=n?4(xaq%C$d|@CFDn@hV52E}-&PTbU!t?Fxk`ZM+BkXP1^BGCyKeA^@Ilo8Z zGc*4#6k=zI+7^Z7*sKIP*ODD+fOeJOescx;az0xk&EX%#EWZpcIZMT4P^6Rc5Or~e@!MBQBPG}l+_%+%^m z3=X3t-UoNM(Ibk61S+>=FCuBu2k(FWeRNr^R;E&4mQx}?J@C>}EXRpM^IKlOc8evP z#~*{7Wz5Bn5=KxzYMN5`?fYgRQoJT|8wNQAxW2y{3R*w-BQa%`+!MQeYjwF~{+KP< zZ$d@m(u23G*!KO0ut3onLXo)tFZ0vDEm4J2Wx8lp9L*2ZtUe(<{>Z-`?=BiBbil(p zO1*per6k52f}Shpo<(`$)`-oku4sE3HITo8SxzY2;wR&F4gam!jS~v`rtUS&qqfI}=wwK4Y zoC|Oy(ord5?psoQq3x1%LDu3mrHIaUKt#~PfkYBbLn+I^!5g`<{*=zT{FGu+6CJYq zML-F*kVh&*S}y4-mh^hvG`L#-O-Q(HGBbn`z0s1&*M<{tThA2yQ= zJZIJ<_7pRA_pq_Uw49f*Q}}L6sMq~`tkaNf%am4w1`Dmcf|>R@R9J-Jm9~Ey=n)Fy z>X83;wAdIJ`l2kN?cAl2QREy-X@MVvTl^M2Za3!jXA(kt2$;%y2qZxZot%=i8jhD= zC5Dksixu}9y5e_MCR?*t1ZwE=R*ekJ)&rBKuL>qQrpSueygnV7Gtt6XSInprpMicJ z9=7;u``W%_e_1ZIM&t7m(^&8=J1fNJp+Sn%lFct8BDnwTwtmaO!EVnZ;Sy1US@j=ogvOYS)k2RAOohsM7 zpgG8GGYAX=y0w_$VLUzYJWUg^lm4gQ519@-sd2FjmseK?A95I(A$BwQ4yVltQ!Ewq)FWmx@cuv`|J*ZuQ{yvi60tM>%DBHct@Df*R*6u7^+7|+ zBq2D*OR_hb3FhV8c#(I0wR~Tg$&l=M%lXe;_Fz5 z@LazxEz<|3wC_?xxerL>SX|93oaH_^Uw#3BRKzj+?n!b%Lvv%F2 zug!}lU|@UcX`ZKxg&oGTEfob_OUxjp54Vlhz05R|0_X75-u48z8AcNbFKC4zy*)kk z6j^brj4#EbgQ>lDy?Q7#qj?#mAGlqwZ_mWQ7`78k(mwSesT#}I=Y{R@jMB}Y;9q)A z?9%9r(Kbx?6(w3^lu*se_&U~QdqlbX6 ze%OTA!{g#+kc$u#ukO|Hvq3C=|M>KxR8CHg0$Dck-O=gE!W@VQ9v*)A6D}@pN7>U; z2B3%nAH{28qNBg}1giZ8i~*Meah6h`W=$cT zz_NZ_7J~;XbAitWbTS33({gg`P!J<6tz%dxrn!n$-2K~x>7$=Nla4PR?(ZF4hO)n> z#>T#03we(_fo6@=3Qro&Hh;IkvBm=7KIG?k5m; zn|z~B954AYG11KlcDOl|iwJ;bOj6ORIXt7EyGj9b&dvOloQ#gsLvFaR+#mfoPvP(br-0Y%qh@ehem1@EGni$V3NfVD=GkOtpJ z?H-tGFD{hjkQM`FsEY;mrA=VJ%Y*dTB{jx9SAEp2Fk($Eli z10caV?LO&r`$lOJdA^%){T>9tX<`t09*e0XgLTz2MuQpMI~N%PGqMJ{381BDUsEc&4Ht z&r{xKq@g1}w)%Zaq6lXj0rk_~v#b`odm< z>-+zlx+Ql4oN_MlU=_KBNav-190q1c9pCv_tIgzhye3ku>Q;Hh+XZFL#KykgpAVkV z7-O28f9l^OJc)7+ZCXJuE#xQkB^_->y!U zlDl@(NT?ay^q&R0wTJwycL+sOs{X(OMDF9`5Vvn(nw&cCZ(6a|_ z0_p!`HznYQ#D&`tk+P5Y7SK84H?9!^mw~_ly!06jL!`FAOCapi=rYMrb3p`$`Y>>{ zY_y|Ar4;lgFay8NnzG? z)3+~v4)B5{QN<0cB*&7mS_`H6f&6@Y3`AL2y~|U{=>r9O*Sv!xBTJui-72TIHU z;ktvqi<$V6rSQ{j^#JTlPBT-Zzd$B#uDg|d^}u(+rr5XeWtVoF)tY28y-oGdh0`_W zE6cxGCcXwiVr~E^;5f;pf^Ow~CD01&MwFgf2O9U9_Jii&$-lK=2#yrpQeFzYTT2S4 zNAV1*LUDH9|2@f>tlVCGn&LB;04Y3bl}t}>kxWn7eTx)?Q~Hy z;44tu2;SwrbZsOdYXjmILgiO>t=1irrPd6BrY8@N7qeZ<2K|9P#w=MoG6zRKLDw0C z`qfXFyKaGtdG!kd#b~;XxZyN)ZMl2+&~8Q`-#I}Rxx%zQ-VvZA5L{vX9&~-O`sO$& z#zH-?`iI%mrl3Akl&(zG@f{Tq}`fg#!+F(kOtq=ADOH0 zZMC1aFN#I`ryN1)FsL+yI>ltW#3qWime%Hh`RlW`RKk! z+DZ|z7SE*Dg&_PFWTkf~QVH|R>nuk>8mU>Q2U@<*mOLR7brpz$xYpv_;!=-SILQMR zlqtl0)(2^Lc}cF==_7@YQY-<6RO$}n9{;6s^D88YtUMX00{-(1^u@)CDYT8_D@_$pSiAPl3vTZjEm`tmjDSK zc(>r{teo;5GJb(TQe1xZiS+bMaJ`=l6C7!Qv-jCfA8eSh{fbi9lb&Py`<|2UXKsF4 z$gXV?mc+f6{BH+%fy2#V=J%glX=lglCC0EDOS{s1>&f7=t1k{7y)8h*2o^7Omh1rn z&Lf{z8C)DWl*;l2{;go%U*x#Z1^A7t)l$G)vrm7Jz_Xqa2T1HmaM9NvoR(A6je!`C z#DEDy0!4LiljG;4zw-1Mn$~u$w~c#c9Yrfcz9V5nj^f$QUM81#S`c!7=)vC}0PiLT z5&-`$$k*M059j zah+dR6xB@xt1Jb0hxBkk@UyY4pqLu7<9P@=FdkN1rPqo@pU~uAuG(C+x_EQir}tH0 zl8qR0J)}MLaWPeLtqTYLKs8T@%INg{Ca{%qG30E~S^xy38)^Q}ekrD^eo}vYW}$1K z!$I5QBGqHIJiyD`sjSTkyTh>5Q+HGuxA&d<418j6xMV4HniM5baCkh~zNjxCkUGe~ zlY9#k=0>Y@Nw<;D$7U3IZ@Nqpn&+(|@62?puZm%Q1IjU|L9Dw?=jUDBd=l%Mtx zD7rOsXKVN^rmE^=w&f4Xt!8Eus0Bv|A4bxA0BT^@N3AQC3L|$!_vL|B8Bpl^=~`2S zAt$qkwc9=qmF&znncLYdF8zsYwjjGi_^}SICf>$gC(rn~3&q@#J0Qfm415gLmRoX3 zpi7_JIX^D$(*H;`Pk4L=X=ZJuFYtdl?RCn)k=z(vv|(%sL=1{`-}Oc#p#l@H`eKNG z((zk^pQyw*3-HPs4Uy5HYZSr94;CZQx8JZT-Q_BNm54BgiZjzgG}p$sErJQO%Og$iKx2uY>eJN24TM)K-ePfCulLnrG z4)}?$j2BoZd$3kiYWtvo5bv{s6Ue@ zI2*{7;s&B?uUO1nJ<$W?&!Q*B!MqCmfa=nqk6mGr86F2=sSD@8jfhSRT1)mvsSTV(pv-XcVbf&onjnyJ zmgghS{3KscfM-h^+0Ca-;wnk&f*^>wE!k?3s1 z>sQTiz}bjX5u6}HtTY_J_#MXq>^$Qe#bW=*pgIT?BUPiv;j4Ql0yM%#F+iaIxqhG# zFO~V8lNoitzo-had8x6qb{%FH3qC;|wEbcEA!HgjgxE|3nDgbJkPD z9>>KnSLcC{6?DijJu%UiUR(Lrn{+s4bciWyf%4i+Xtv%Hb7-NxaxmER%E$~peDpqy z1u4Myt2iMkVjcoIrqt+9*t#fal9u6`?H7Lu(Hvzz+YM8cx(ty4)&-#P*a-5EVd~i9 zL+)2{a)}HSjX3oZH%_EU)-&+$`3PwIOv%M;$FG>)B?|}6ABmM0ixuo}B-jR->pebB8@=jDG-n9kb)p>K z)+p)^hV*wB%f6nxmlq33{okT>f6Z~pS`c6t@0f3S-K2i_19=5VF>+yFEWamJ#UEhU zYrrDOe zAg1bVXuVT6r6)Ug6}v>;3Ii<<4K|<(0D1=~kvqy*prh#vjif1imky1T>k@)*fqCX2 zNZ4~k^p_LXu83n9_$7JvdVc$*3q}!^lbI0Fp?B8u)wlYPWl8$qlt#;b;ik)hMvRXW z^F=hYcoRy1!ZyJhD=T&Y>OIA6d+QePjc}^6KCk~Zx;X+NBg?BejE7e~-K+Q{G;|M8 zUoKy=XAHY3n{$IeIit@K` z5V1ZnKl&GeC$mPIe*6x6&47R5WZ<6R=W!(x3w|xHwY`X=y7x-)Z4<0OY!3MrS)!?x z>YXvwTu`p2Zm@3FW~=J*;ci5>n1GdGPAn{v3avGmA{EZ%qka;L&BdR8u<&9Q^6UxLv=Oe60(-!!(1YFo;@3a~g`&(w#N`3S zu0H14;@c_R4-FQDmxi49zjx@ZRhqb(*p-?Aoc`y(u={J6*D{8kmgmZlnjFHv%7npe zPODF*v<$UghiOM0X_0liUK{Fyj<1bq7(SNc)qiqs%KW4p)LDeIACaVa+o$ffM~|1< z9UYjH(CZ81C|48}Sn;D>;c@cR`S@E21fqGRC?~B=_%fJ@0)S5biS!>zR;Y)C?r!JJwUL%GCnNtZ#cuDBp^|jeS zox1nH4SWWRoet5zb-AWe)5_=`J$j#GZKbJ+eEPpIc=5=zi5}*mJJ(R45E-8m-2uSi zc_=&wrjnd(2?vD@V(MZ7HUUVui*#+^{||)^fY(=YX;hP?RY(0&AChId@C93`wsj!= z$!8)e#f6%0SNt$Bf9V|aNLXMcVDe43My>*`ut|BZUj*(&I37_+mPzmfjeeUxOA3VD1i+n3(1%*IL4+s}i*ZnwYF%+qtNd9l`d?oRfV2d<`1lnRt4 z9KQQx*(0|l$(h5n=)@Upv;PbQTKfn&9>q9?AE}%=eJ#l5Pg8_KB)jub6T^1 zf+GvB8bQ$j+I&nM3t)x_2GtkydOtyppHt|8CdB^&?SXIKHbgu_?{L<|Gov^@h`-Wb zzNv=?n*7fbF7qSyU=08SwjmNZwNCAUC($QV1rSS3H&`36wPaGw)voyu(r!VCVO=;g zBnH6mP+pw0JrMviW`X}7pdDJG2q2-lR~W)dTJ|7WAgS1A4(PT3W3}?4(KHH~%iNGd z_Z0y0R>@`(n*1gNlJ}*=19GU=fbGF<^vtMy=ba+}-b$&r00r;I<-oAkNL%ZwH)l8i zXW)K&?R@;>`57*c^1ePD9|HvJa}X4c2kcE*t+mV6-lp-fLl8ymJr=NljTrGXmgzhI zuJhVXnKQ8i4@j+XT3_$KX=`VLAS!(n7Z;!3_~6wLFAGRaZ;i==yjpfqy=q7atpuEZ zEb$u~DIlk2%FBsNU2WbfhdPiz$%EjS*|y&2dV{v_@iAuX)ph&yRC03kDX0@j)(EX| zIbkPK(R_i-n&N8$5CF9PoB*uZa!YW)YD`;7WWNr|6foz@E#sgv8P#k`X43NZ^LypB zHwD<{9WQtlk|3o^&6`5u43EWS^Ev%?cpD%H{})sbPniF(sKkzK{wOoVNpxwvNSp`& z%&#ZNtkvj>Jb%>LfD_W}ba((wpz%~^NN-$%#y+*a%K>zSxRzw3DgLz`R45j}6Ojcx z(QS+9ehE$(bL%_@Hd?(-d+8Z~cD?_OaiRr>1J*jlScM(Xr~r1*6$VtmBpc1o6G@H- zJ=q4Nv4OB0DqN*=h(R>{X0A;E>!VwfQD)UWn-_^IFqu0D*i<{GxX9n6aIit<_a+_f zfHthwFIv_1%}fE%ldOhx0MNLvyzdkXJw$lv*>&M^s=T<80k=zYK8Sb@7VwJAN$*=! z_rQ^uv|UL9Q57BO{*kmJG;_6fAd;aIg|YO3gw_w#b_sytI(oBn}7R!#)KYSV_BY9fdF zFUbehOTYifVHFYjPkggG0RYJR4gkciX|>rRq9ZAJb93|Hdpu`hbAx<55iHYfwYB2< z$tKH$=o`V6Kv03l7-`%Cv(o$VBOW0k{q=F+iui`zhT^{*^wO~u0G4A)yn?!4(zVFa zI7mEwD-l-#3tUFee91lS$8~xBLK4e5!SJGSXD%My4q$ZXYo=`< zo=o|69_VXR=V%nA!3i~8mspc5FNHkl7LO|on$4wy@R!#eiTR8RGvW$E~FI9uov%d>tUwZQcrgUF6*oJG0|X2 zb$KBX)V(b@#Is>9Dh@$RGLcw3TCgE~GR5%;pW`0d1Si;09~ETe1T4zKN*`~4{OJZC z6MAn=w6*@Xej{$-Hr?}?3=nmRyB}+IzXJOEaz6jM39o(=DaQFl6f!?*cvd|%mG`q& zr_S+jq5yBdYbvZxZ$-ju*}zU1{)?b{Gvyi?Hl-Lz)&yt&<$}=GEC8PvuD2g6IQ@hl z55LJ|HzF$Vni2M}qj5$T_doy3s@K?lUC>?-m<+3@aHps{fggYO_gY-{M6YwZ)%#HE z*oz|oxN3IqBjFP|F-HP`_7FaEDV!(s<&!%aG!Fumgx=TFyGI@7`|TOSE8x>$w@7A@~2evWu(zH=U$z35*V??BKvYXe9mbH=u5#W5B`M zy4?3d{t)Rb$0eLbQ2K(JZkX^&CQwMiDY=8zBi- zr&4}D?)W6-LBTrAFxygU9duMvHKt)WCUW~`Goqw~=J?j+H^Q$}24%;hd7U{SQv;-K z4irT|S4van%SD7VD{K2_@(VX{ST@AQ?$Rz44oX83bL219GtR)FM`vNx@BRyv=Rjn$ z1-}Md?2QUKf8hQ;7K18qX@^jYu-k^>+P9A{q$nuO+pUSGh=!H!>HTuP16zua0YLG6 zxm{LS)3{JI2iE=^0);S+^uI`Y*8o$o;Nx8TNKPI4J5hMH#o z!O~dc%i{lTczlL02}j?DO(8;n31r}`6CGqTuMZ94_UBhC$pp&qLV)80iOYxIg>{a< z(%-1nG(@mS2S}=e%KlduR~`su_qIo>A!|t{8d6D%>?zAoh~$mP7BOTQnJg`1U#7B0 zD*J1D4U#RCHHMMwTZ1SgWY5HqVKB^mXXyQXzweuWX6AX$bDwjc^*YymohRQgK_Wg* zaqxzuby0x^N?tW4S>ggd%~Qk7=n*9M`lrW>a+2eh;sNeUB588&7w3L+i%37<-27rb zDJbTj{ezZ48BQMV!&Stg*P&v&EQB;vmX2%7Naja|?)ET!{m(%?Htyb&p-RQsb4u^y zq6i7J<=rJm*jGbVsC9}DcX14=->}XD*@pGZRxB8(urj58WN51Ra>gYr(VC^Mkc}OW zzb$8|=S&E$zBIuX{RoS=hi-}#9C(1s=SY6ltBPGQXzvyzI6;N2&xfbvmW5>sL{&=} zLiE38vAi|5@Q(9*7mlWUW;!&-Q9nit6%}#a?pI)d^6&|6}h>JeRNFClyKSkf!5$6$! z7_dS@8}K4DkKvB{hAntFZLKbcKH*o_{n!%+c!G&tRj9?u(h>xW$E{kKVXPCgnYhvf z0=CuBqJgsA*oLkPEy=Q|qxh!|3Oi)Y!cVb?y=KV$JQ>S-$ofZ*0qv7EGruEC8DA7o z_PX0Jv4VGOWIZLTVcsh${()GTv?8$fir7fgeM)kBrl37@O#u)&cRL^pR{lDQj|Pxd zLh<3@r*^;Fos4_<`X!5kFon=$5E{bjGs^>e02Ivc@Ii(rxFVLcRjK_g4%cA0F?y&+ zhrZ~~W-es$z}Zb^?9^f;hH8ZVr6B+4=#-N^(BoioPcvBZa-tvfM#R7Bm0_yUAXK^0=>9 zM!&hb2ULUVseg z38@0nq8~<8Zn0zmPYUO(Cj-qY`2cso!#Bo=Z^0R@J^)e=UO8WCroc_hlMa7x-{To$ zoHw_U1o8ZS&JFY1ccykObB)#I%*4d|i8YI`4C$h z+%2JoAj>u#-eCb$YP=sOaN?jaL90gn4xH8ghmTG?T96>F|1`FM(275Fs#pN8P{9in1#hBW_^3dw4iI1uviY{P z2^$6@u2XgoK{X#0{>D5{CIH9UxxZaN85E~I>)L1QSqrkre}HN21-Y6-Vtz3{U;s+j zHub2mga{WO49N%`pUmpegW@1e50#IfcJw^YGM!Rb!ySO%N|YZJi! ze1#VjboNvBkv~j{+IsfSdrXOA#~xf#U7U;+zOe^U9+XzD0CrQtVSkFNAcS<72eldA z{D&@D{PktfN);r*-T#+x@fv~_!hxs6s`PV|^4}~SC*3RH;}95vGjDsCUX#lzj5`On zR%3!90~h;*a6MaeF`Rwn*2f{UT<^6Rnf`9_lozAj(xCtDpXj!?Eo}yvG`jE4e(@)! zhn5lTS
BP$I$EliK(Gs})VB3!-(kO^zsFnQO!PM4D)zG%-LUP<^AVI7U~7kyqin z34T8Jy3L`0&Aa!n>9sL0U>ftz+I{U-*;vw6U_lbw=4V4>Ndo#}pvmQWs`0!%ad5$} z*|IoI?!TU!Y~^D;hjTPnI}|X%K#iv`deYTIi)uISqtnbdcx;z680(egJ>pTc;kAe8 zG|wi;>vxxTNF7_e0M4Lnv%Lk!q&LpETnMOrURQunk(=Tfi=teOLov~FQ!|b*kc?%<$=zvZNzBqGl zuUHKGwE2tsCq{?7v#B(l;~aBm+HQt5wbaLn63pwEi9`+mWj^9Wy^848LUQ7`-SxF) zsD_XD#wQDJ3ioVlIW@8c=M}V(H(R6OFTK%aary{J6Z$Sg-A8RBm$E6RU=qL%%h@bu z%tSb`*VaDNiH@rk zO899>Vr)H}&mws3yjUu1U~QPhT+OReJXf48ZhV(5YOx758L}84(3q%#1%lwcvhlrh z=4$FcrlpH18XLE%^I~dNPrLPf7|e8N`srF>MTcX&Wut!!+*e!em>`FpcAgVG>NAxV`CHdMXLFDdQuD!k)GAOEu|n5q;!o1cvFtOa!1>e> z*>~v{m$FWrDK4Ot6hxQ%i2{~=t1T$)>t(6a*w~M>E;0Kx9LY~Rz+B9-a?OR=#FZA@d1k7%Zhh>1hI;|adP(UTonIXpe;x}$POY*j!s{sZDwC? zE|1p20@nR{KJh)gWGXY{LcGjnXz?nM*S6DJq8O)7@J9Upv^48pCtu3`U6*lxT#mn7 zbvxRUzdKk~bI`xsiepfOF#RYlgE=kcLaCoyh{&<0UQ=f}(F9_9cYQtbgqrt+grx*g z*Q+9rP?#M-QA}HcM}dg?_`=lnez%>0C(mEOT*`_Nk76Q!Im@_K__D8!=r-%GPkUq- z3defbDoJfAgEu*3v%?x+O@>(R+4^>(#f^T2%IHyt*DPoWJT;*`NuZhE-ufDxTqFCu zx>X#h9i7bz&;J$wihA+maY|v(tigUTJ(U;xtEJa!*~}89Iy#NXx%KHbE>{H0zGdn| zWt~RlB?qd0dHUga=L568TbM`J?1FW#EoWR$nh;KVr^H|$kT+88+g?w-TT4bbjU+z` zypW9*v@v9?HaR<{P|J!Wv0bG#FdYX2g^2t+K6YzyjKa25b79arSWkPOtu>(*^p|Em z*--P>?VEl2t1)S1kiFb7@?lr(PRiqXjZ}ALZoV-s+AY;%a}Iv)n?6?YZ+-*Cqz2gq zW1^fw9?RVKTN5_!(?Bjx_8vLq`uFm+jJdYcW|+v)6R<}(ojRkYQB^%TVL%vXCKiye7x2x&ys z$=O7Ppi-`R9;>=+`af z?L~ML(p-}2{V@u3dvrq}0-BR&%=^?~no{ zJi=-O4Tk{8_aC8b-`+|KC;+S`S6a2xwpHmR3Wrz>VS2YYpbqV8W7YYA&u2K{baXpV zmsioa1{#0>+q7Y9M8>ijhITh3mm|9q{SFn-OBJjO!Q9La;{%f!1c{bA!-aC50U(|x&UfDP11{C;`E z-+bG|J;+Yp@tW&Q>05^LB#v?m%3{(_9*5SSPw)v4kmpZ+^N_{&16B3LCx~btB1keL z4%AIfNEA7}vawym0oSc3*1&czK9DDXJYox%{de0Cp_VfSl)@^Q{PKzW3R%^tpdl3? zkH;A(dC^egQSTp8b~|Ksvud%>E59ohafF8gp#CDToyL(IdautX_h)VAfrA8j>Zy)G z#?-Gkpk;>h-3tAyPdU+{H(PI~MYlt6nfkQW0cYxLOkpe_wdZ!X$~q)>Ul4N0?q{ z;wn^1h6ENfvyOG%3cRoAyG9=fVeLXg!6r-ly-)^v>Lx-n{rM!Ekg4^;zjMy| z2+zW13C0MtxbR(%8C_g5{#oJeeDl^DCn%5s)zikbO>5#J!`(s%qfpiTn>yK!zw`qVGj)D18?O4SRup z2T=Ms;vySLBL^{j?(cqQeFLR<^cN(>X+SX{|LwqXwR;M?d5SJELenfyM1t}+#O(}i^F!=t?NBjDHy%=P<+@`{o6 z__LByl)1V5bL@pR&My&xu0V#pgmOfrKD9vscBp&`yzeNovS{$IuyAC>y>G^(L?6a< z>Dcndd+|>6Fo^`z(ST_E@?2{G@NsqJqJ=j24;9ZD-P_XM3a75Woq_0eU`AI*Cw0bp z(o^%TxcXW0ol#$9BT2&O1d&K|Zg{e13D3n04GRm)0_Q}~-*2qAi7-W%eEbi2hW{ZsZLUk`PY)JA}^Ry9WAtlGP^-LipYttt;{6;Rz}15e8!%^GK_2R3R; z`Ic*~H9UZ7uJybFW6z4a%Ju!E<^brPqst0FEqB8N!d_>h(62cB z37iVFqBDUJ9+H%u#ASZg(Y+S&>CR*UXf-1>FMP3eT!649h_+ERKesD;kL;LOVRg@m zuR?)(5u{G7HdJH`&<4?lG}lNyntFjjB$;L2Lkg{eYFv)>xGTkG`d4SB6g5~j~iHXv`O zN(Ms4mN_t|O>1_^{j~V~o747%ZIj$aVd6?!SIPyMx9SPYq2PVQp9U3N>6Rn&!MC;c z%3{t0|Fb*$Z2t{d>qBHQiew?i^mIJDPjQXb4)Of_gVL)cpVf#na<+p=zCfyQ+XA^fW^ZuraPiV8keO{cl%y}*K22eN z0@5CTw7=EqvpkT3e%Uph*Z`>*6bCa;mHTC5dV${19SkQd0~3!WfAciXl>!y;46THl zz{z3{6SPf4Pc^g$s27cDi;9z+AR>zeI>K!^FX8o;Fan4I&iQlbKKd&j`>DzrZ(!q7 z(FIYuicygM#{{wcbbocHllJ2?)5jKOR@`b#**2LFjo1=yk(cHW)<^G(f%aQX+b916 DUVzwb literal 0 HcmV?d00001 diff --git a/app/screenshots/sideplay_artist.png b/app/screenshots/sideplay_artist.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7f5c94a42a88a18f1cc8d3435b6009a955fa8a GIT binary patch literal 50204 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

, + size_limit: Option, + ) -> Result { + let mut size_limiter = None; + + if let Some(location) = &credentials_path { + fs::create_dir_all(location)?; + } + + let credentials_location = credentials_path + .as_ref() + .map(|p| p.as_ref().join("credentials.json")); + + if let Some(location) = &volume_path { + fs::create_dir_all(location)?; + } + + let volume_location = volume_path.as_ref().map(|p| p.as_ref().join("volume")); + + if let Some(location) = &audio_path { + fs::create_dir_all(location)?; + + if let Some(limit) = size_limit { + let limiter = FsSizeLimiter::new(location.as_ref(), limit)?; + size_limiter = Some(Arc::new(limiter)); + } + } + + let audio_location = audio_path.map(|p| p.as_ref().to_owned()); + + let cache = Cache { + credentials_location, + volume_location, + audio_location, + size_limiter, + }; + + Ok(cache) + } + + pub fn credentials(&self) -> Option { + let location = self.credentials_location.as_ref()?; + + // This closure is just convencience to enable the question mark operator + let read = || -> Result { + let mut file = File::open(location)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + Ok(serde_json::from_str(&contents)?) + }; + + match read() { + Ok(c) => Some(c), + Err(e) => { + // If the file did not exist, the file was probably not written + // before. Otherwise, log the error. + if e.kind != ErrorKind::NotFound { + warn!("Error reading credentials from cache: {e}"); + } + None + } + } + } + + pub fn save_credentials(&self, cred: &Credentials) { + if let Some(location) = &self.credentials_location { + let result = File::create(location).and_then(|mut file| { + let data = serde_json::to_string(cred)?; + write!(file, "{data}") + }); + + if let Err(e) = result { + warn!("Cannot save credentials to cache: {e}") + } + } + } + + pub fn volume(&self) -> Option { + let location = self.volume_location.as_ref()?; + + let read = || -> Result { + let mut file = File::open(location)?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + Ok(contents.parse()?) + }; + + match read() { + Ok(v) => Some(v), + Err(e) => { + if e.kind != ErrorKind::NotFound { + warn!("Error reading volume from cache: {e}"); + } + None + } + } + } + + pub fn save_volume(&self, volume: u16) { + if let Some(ref location) = self.volume_location { + let result = File::create(location).and_then(|mut file| write!(file, "{volume}")); + if let Err(e) = result { + warn!("Cannot save volume to cache: {e}"); + } + } + } + + pub fn file_path(&self, file: FileId) -> Option { + match file.to_base16() { + Ok(name) => self.audio_location.as_ref().map(|location| { + let mut path = location.join(&name[0..2]); + path.push(&name[2..]); + path + }), + Err(e) => { + warn!("Invalid FileId: {e}"); + None + } + } + } + + pub fn file(&self, file: FileId) -> Option { + let path = self.file_path(file)?; + match File::open(&path) { + Ok(file) => { + if let Some(limiter) = self.size_limiter.as_deref() { + if !limiter.touch(&path) { + error!("limiter could not touch {path:?}"); + } + } + Some(file) + } + Err(e) => { + if e.kind() != io::ErrorKind::NotFound { + warn!("Error reading file from cache: {e}") + } + None + } + } + } + + pub fn save_file(&self, file: FileId, contents: &mut F) -> Result { + if let Some(path) = self.file_path(file) { + if let Some(parent) = path.parent() { + if let Ok(size) = fs::create_dir_all(parent) + .and_then(|_| File::create(&path)) + .and_then(|mut file| io::copy(contents, &mut file)) + { + if let Some(limiter) = self.size_limiter.as_deref() { + limiter.add(&path, size); + limiter.prune()?; + } + return Ok(path); + } + } + } + Err(CacheError::Path.into()) + } + + pub fn remove_file(&self, file: FileId) -> Result<(), Error> { + let path = self.file_path(file).ok_or(CacheError::Path)?; + + fs::remove_file(&path)?; + if let Some(limiter) = self.size_limiter.as_deref() { + limiter.remove(&path); + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::time::Duration; + + fn ordered_time(v: u64) -> SystemTime { + SystemTime::UNIX_EPOCH + Duration::from_secs(v) + } + + #[test] + fn test_size_limiter() { + let mut limiter = SizeLimiter::new(1000); + + limiter.add(Path::new("a"), 500, ordered_time(2)); + limiter.add(Path::new("b"), 500, ordered_time(1)); + + // b (500) -> a (500) => sum: 1000 <= 1000 + assert!(!limiter.exceeds_limit()); + assert_eq!(limiter.pop(), None); + + limiter.add(Path::new("c"), 1000, ordered_time(3)); + + // b (500) -> a (500) -> c (1000) => sum: 2000 > 1000 + assert!(limiter.exceeds_limit()); + assert_eq!(limiter.pop().as_deref(), Some(Path::new("b"))); + // a (500) -> c (1000) => sum: 1500 > 1000 + assert_eq!(limiter.pop().as_deref(), Some(Path::new("a"))); + // c (1000) => sum: 1000 <= 1000 + assert_eq!(limiter.pop().as_deref(), None); + + limiter.add(Path::new("d"), 5, ordered_time(2)); + // d (5) -> c (1000) => sum: 1005 > 1000 + assert_eq!(limiter.pop().as_deref(), Some(Path::new("d"))); + // c (1000) => sum: 1000 <= 1000 + assert_eq!(limiter.pop().as_deref(), None); + + // Test updating + + limiter.add(Path::new("e"), 500, ordered_time(3)); + // c (1000) -> e (500) => sum: 1500 > 1000 + assert!(limiter.update(Path::new("c"), ordered_time(4))); + // e (500) -> c (1000) => sum: 1500 > 1000 + assert_eq!(limiter.pop().as_deref(), Some(Path::new("e"))); + // c (1000) => sum: 1000 <= 1000 + + // Test removing + limiter.add(Path::new("f"), 500, ordered_time(2)); + assert!(limiter.remove(Path::new("c"))); + assert!(!limiter.exceeds_limit()); + } +} diff --git a/native/vendor/librespot-core/src/cdn_url.rs b/native/vendor/librespot-core/src/cdn_url.rs new file mode 100644 index 00000000..818f68f5 --- /dev/null +++ b/native/vendor/librespot-core/src/cdn_url.rs @@ -0,0 +1,259 @@ +use std::ops::{Deref, DerefMut}; + +use protobuf::Message; +use thiserror::Error; +use time::Duration; +use url::Url; + +use super::{Error, FileId, Session, date::Date}; + +use librespot_protocol as protocol; +use protocol::storage_resolve::StorageResolveResponse as CdnUrlMessage; +use protocol::storage_resolve::storage_resolve_response::Result as StorageResolveResponse_Result; + +#[derive(Debug, Clone)] +pub struct MaybeExpiringUrl(pub String, pub Option); + +const CDN_URL_EXPIRY_MARGIN: Duration = Duration::seconds(5 * 60); + +#[derive(Debug, Clone)] +pub struct MaybeExpiringUrls(pub Vec); + +impl Deref for MaybeExpiringUrls { + type Target = Vec; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for MaybeExpiringUrls { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[derive(Debug, Error)] +pub enum CdnUrlError { + #[error("all URLs expired")] + Expired, + #[error("resolved storage is not for CDN")] + Storage, + #[error("no URLs resolved")] + Unresolved, +} + +impl From for Error { + fn from(err: CdnUrlError) -> Self { + match err { + CdnUrlError::Expired => Error::deadline_exceeded(err), + CdnUrlError::Storage | CdnUrlError::Unresolved => Error::unavailable(err), + } + } +} + +#[derive(Debug, Clone)] +pub struct CdnUrl { + pub file_id: FileId, + urls: MaybeExpiringUrls, +} + +impl CdnUrl { + pub fn new(file_id: FileId) -> Self { + Self { + file_id, + urls: MaybeExpiringUrls(Vec::new()), + } + } + + pub async fn resolve_audio(&self, session: &Session) -> Result { + let file_id = self.file_id; + let response = session.spclient().get_audio_storage(&file_id).await?; + let msg = CdnUrlMessage::parse_from_bytes(&response)?; + let urls = MaybeExpiringUrls::try_from(msg)?; + + let cdn_url = Self { file_id, urls }; + + trace!("Resolved CDN storage: {cdn_url:#?}"); + + Ok(cdn_url) + } + + #[deprecated = "This function only returns the first valid URL. Use try_get_urls instead, which allows for fallback logic."] + pub fn try_get_url(&self) -> Result<&str, Error> { + if self.urls.is_empty() { + return Err(CdnUrlError::Unresolved.into()); + } + + let now = Date::now_utc(); + let url = self.urls.iter().find(|url| match url.1 { + Some(expiry) => now < expiry, + None => true, + }); + + if let Some(url) = url { + Ok(&url.0) + } else { + Err(CdnUrlError::Expired.into()) + } + } + + pub fn try_get_urls(&self) -> Result, Error> { + if self.urls.is_empty() { + return Err(CdnUrlError::Unresolved.into()); + } + + let now = Date::now_utc(); + let urls: Vec<&str> = self + .urls + .iter() + .filter_map(|MaybeExpiringUrl(url, expiry)| match *expiry { + Some(expiry) => { + if now < expiry { + Some(url.as_str()) + } else { + None + } + } + None => Some(url.as_str()), + }) + .collect(); + + if urls.is_empty() { + Err(CdnUrlError::Expired.into()) + } else { + Ok(urls) + } + } +} + +impl TryFrom for MaybeExpiringUrls { + type Error = crate::Error; + fn try_from(msg: CdnUrlMessage) -> Result { + if !matches!( + msg.result.enum_value_or_default(), + StorageResolveResponse_Result::CDN + ) { + return Err(CdnUrlError::Storage.into()); + } + + let is_expiring = !msg.fileid.is_empty(); + + let result = msg + .cdnurl + .iter() + .map(|cdn_url| { + let url = Url::parse(cdn_url)?; + let mut expiry: Option = None; + + if is_expiring { + let mut expiry_str: Option = None; + if let Some(token) = url + .query_pairs() + .into_iter() + .find(|(key, _value)| key == "verify") + { + // https://audio-cf.spotifycdn.com/audio/844ecdb297a87ebfee4399f28892ef85d9ba725f?verify=1750549951-4R3I2w2q7OfNkR%2FGH8qH7xtIKUPlDxywBuADY%2BsvMeU%3D + if let Some((expiry_str_candidate, _)) = token.1.split_once('-') { + expiry_str = Some(expiry_str_candidate.to_string()); + } + } else if let Some(token) = url + .query_pairs() + .into_iter() + .find(|(key, _value)| key == "__token__") + { + //"https://audio-ak-spotify-com.akamaized.net/audio/4712bc9e47f7feb4ee3450ef2bb545e1d83c3d54?__token__=exp=1688165560~hmac=4e661527574fab5793adb99cf04e1c2ce12294c71fe1d39ffbfabdcfe8ce3b41", + if let Some(mut start) = token.1.find("exp=") { + start += 4; + if token.1.len() >= start { + let slice = &token.1[start..]; + if let Some(end) = slice.find('~') { + // this is the only valid invariant for akamaized.net + expiry_str = Some(String::from(&slice[..end])); + } else { + expiry_str = Some(String::from(slice)); + } + } + } + } else if let Some(token) = url + .query_pairs() + .into_iter() + .find(|(key, _value)| key == "Expires") + { + //"https://audio-gm-off.spotifycdn.com/audio/4712bc9e47f7feb4ee3450ef2bb545e1d83c3d54?Expires=1688165560~FullPath~hmac=IIZA28qptl8cuGLq15-SjHKHtLoxzpy_6r_JpAU4MfM=", + if let Some(end) = token.1.find('~') { + // this is the only valid invariant for spotifycdn.com + let slice = &token.1[..end]; + expiry_str = Some(String::from(&slice[..end])); + } + } else if let Some(query) = url.query() { + //"https://audio4-fa.scdn.co/audio/4712bc9e47f7feb4ee3450ef2bb545e1d83c3d54?1688165560_0GKSyXjLaTW1BksFOyI4J7Tf9tZDbBUNNPu9Mt4mhH4=", + let mut items = query.split('_'); + if let Some(first) = items.next() { + // this is the only valid invariant for scdn.co + expiry_str = Some(String::from(first)); + } + } + + if let Some(exp_str) = expiry_str { + if let Ok(expiry_parsed) = exp_str.parse::() { + if let Ok(expiry_at) = Date::from_timestamp_ms(expiry_parsed * 1_000) { + let with_margin = expiry_at.saturating_sub(CDN_URL_EXPIRY_MARGIN); + expiry = Some(Date::from(with_margin)); + } + } else { + warn!( + "Cannot parse CDN URL expiry timestamp '{exp_str}' from '{cdn_url}'" + ); + } + } else { + warn!("Unknown CDN URL format: {cdn_url}"); + } + } + Ok(MaybeExpiringUrl(cdn_url.to_owned(), expiry)) + }) + .collect::, Error>>()?; + + Ok(Self(result)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_maybe_expiring_urls() { + let timestamp = 1688165560; + let mut msg = CdnUrlMessage::new(); + msg.result = StorageResolveResponse_Result::CDN.into(); + msg.cdnurl = vec![ + format!( + "https://audio-cf.spotifycdn.com/audio/844ecdb297a87ebfee4399f28892ef85d9ba725f?verify={timestamp}-4R3I2w2q7OfNkR%2FGH8qH7xtIKUPlDxywBuADY%2BsvMeU%3D" + ), + format!( + "https://audio-ak-spotify-com.akamaized.net/audio/foo?__token__=exp={timestamp}~hmac=4e661527574fab5793adb99cf04e1c2ce12294c71fe1d39ffbfabdcfe8ce3b41" + ), + format!( + "https://audio-gm-off.spotifycdn.com/audio/foo?Expires={timestamp}~FullPath~hmac=IIZA28qptl8cuGLq15-SjHKHtLoxzpy_6r_JpAU4MfM=" + ), + format!( + "https://audio4-fa.scdn.co/audio/foo?{timestamp}_0GKSyXjLaTW1BksFOyI4J7Tf9tZDbBUNNPu9Mt4mhH4=" + ), + "https://audio4-fa.scdn.co/foo?baz".to_string(), + ]; + msg.fileid = vec![0]; + + let urls = MaybeExpiringUrls::try_from(msg).expect("valid urls"); + assert_eq!(urls.len(), 5); + assert!(urls[0].1.is_some()); + assert!(urls[1].1.is_some()); + assert!(urls[2].1.is_some()); + assert!(urls[3].1.is_some()); + assert!(urls[4].1.is_none()); + let timestamp_margin = Duration::seconds(timestamp) - CDN_URL_EXPIRY_MARGIN; + assert_eq!( + urls[0].1.unwrap().as_timestamp_ms() as i128, + timestamp_margin.whole_milliseconds() + ); + } +} diff --git a/native/vendor/librespot-core/src/channel.rs b/native/vendor/librespot-core/src/channel.rs new file mode 100644 index 00000000..dfdc4dc2 --- /dev/null +++ b/native/vendor/librespot-core/src/channel.rs @@ -0,0 +1,229 @@ +use std::{ + collections::HashMap, + fmt, + pin::Pin, + task::{Context, Poll}, + time::{Duration, Instant}, +}; + +use byteorder::{BigEndian, ByteOrder}; +use bytes::Bytes; +use futures_core::Stream; +use futures_util::{StreamExt, lock::BiLock, ready}; +use num_traits::FromPrimitive; +use thiserror::Error; +use tokio::sync::mpsc; + +use crate::{Error, packet::PacketType, util::SeqGenerator}; + +component! { + ChannelManager : ChannelManagerInner { + sequence: SeqGenerator = SeqGenerator::new(0), + channels: HashMap> = HashMap::new(), + download_rate_estimate: usize = 0, + download_measurement_start: Option = None, + download_measurement_bytes: usize = 0, + invalid: bool = false, + } +} + +const ONE_SECOND: Duration = Duration::from_secs(1); + +#[derive(Debug, Error, Hash, PartialEq, Eq, Copy, Clone)] +pub struct ChannelError; + +impl From for Error { + fn from(err: ChannelError) -> Self { + Error::aborted(err) + } +} + +impl fmt::Display for ChannelError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "channel error") + } +} + +pub struct Channel { + receiver: mpsc::UnboundedReceiver<(u8, Bytes)>, + state: ChannelState, +} + +pub struct ChannelHeaders(BiLock); +pub struct ChannelData(BiLock); + +pub enum ChannelEvent { + Header(u8, Vec), + Data(Bytes), +} + +#[derive(Clone)] +enum ChannelState { + Header(Bytes), + Data, + Closed, +} + +impl ChannelManager { + pub fn allocate(&self) -> (u16, Channel) { + let (tx, rx) = mpsc::unbounded_channel(); + + let seq = self.lock(|inner| { + let seq = inner.sequence.get(); + if !inner.invalid { + inner.channels.insert(seq, tx); + } + seq + }); + + let channel = Channel { + receiver: rx, + state: ChannelState::Header(Bytes::new()), + }; + + (seq, channel) + } + + pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) -> Result<(), Error> { + use std::collections::hash_map::Entry; + + let id: u16 = BigEndian::read_u16(data.split_to(2).as_ref()); + + self.lock(|inner| { + let current_time = Instant::now(); + if let Some(download_measurement_start) = inner.download_measurement_start { + if (current_time - download_measurement_start) > ONE_SECOND { + inner.download_rate_estimate = ONE_SECOND.as_millis() as usize + * inner.download_measurement_bytes + / (current_time - download_measurement_start).as_millis() as usize; + inner.download_measurement_start = Some(current_time); + inner.download_measurement_bytes = 0; + } + } else { + inner.download_measurement_start = Some(current_time); + } + + inner.download_measurement_bytes += data.len(); + + if let Entry::Occupied(entry) = inner.channels.entry(id) { + entry + .get() + .send((cmd as u8, data)) + .map_err(|_| ChannelError)?; + } + + Ok(()) + }) + } + + pub fn get_download_rate_estimate(&self) -> usize { + self.lock(|inner| inner.download_rate_estimate) + } + + pub(crate) fn shutdown(&self) { + self.lock(|inner| { + inner.invalid = true; + // destroy the sending halves of the channels to signal everyone who is waiting for something. + inner.channels.clear(); + }); + } +} + +impl Channel { + fn recv_packet(&mut self, cx: &mut Context<'_>) -> Poll> { + let (cmd, packet) = ready!(self.receiver.poll_recv(cx)).ok_or(ChannelError)?; + + let packet_type = FromPrimitive::from_u8(cmd); + if let Some(PacketType::ChannelError) = packet_type { + let code = BigEndian::read_u16(&packet.as_ref()[..2]); + error!("channel error: {} {}", packet.len(), code); + + self.state = ChannelState::Closed; + + Poll::Ready(Err(ChannelError)) + } else { + Poll::Ready(Ok(packet)) + } + } + + pub fn split(self) -> (ChannelHeaders, ChannelData) { + let (headers, data) = BiLock::new(self); + + (ChannelHeaders(headers), ChannelData(data)) + } +} + +impl Stream for Channel { + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + match self.state.clone() { + ChannelState::Closed => { + error!("Polling already terminated channel"); + return Poll::Ready(None); + } + + ChannelState::Header(mut data) => { + if data.is_empty() { + data = ready!(self.recv_packet(cx))?; + } + + let length = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; + if length == 0 { + self.state = ChannelState::Data; + } else { + let header_id = data.split_to(1).as_ref()[0]; + let header_data = data.split_to(length - 1).as_ref().to_owned(); + + self.state = ChannelState::Header(data); + + let event = ChannelEvent::Header(header_id, header_data); + return Poll::Ready(Some(Ok(event))); + } + } + + ChannelState::Data => { + let data = ready!(self.recv_packet(cx))?; + if data.is_empty() { + self.receiver.close(); + self.state = ChannelState::Closed; + return Poll::Ready(None); + } else { + let event = ChannelEvent::Data(data); + return Poll::Ready(Some(Ok(event))); + } + } + } + } + } +} + +impl Stream for ChannelData { + type Item = Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut channel = ready!(self.0.poll_lock(cx)); + + loop { + match ready!(channel.poll_next_unpin(cx)?) { + Some(ChannelEvent::Header(..)) => (), + Some(ChannelEvent::Data(data)) => return Poll::Ready(Some(Ok(data))), + None => return Poll::Ready(None), + } + } + } +} + +impl Stream for ChannelHeaders { + type Item = Result<(u8, Vec), ChannelError>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut channel = ready!(self.0.poll_lock(cx)); + + match ready!(channel.poll_next_unpin(cx)?) { + Some(ChannelEvent::Header(id, data)) => Poll::Ready(Some(Ok((id, data)))), + _ => Poll::Ready(None), + } + } +} diff --git a/native/vendor/librespot-core/src/component.rs b/native/vendor/librespot-core/src/component.rs new file mode 100644 index 00000000..75387ae5 --- /dev/null +++ b/native/vendor/librespot-core/src/component.rs @@ -0,0 +1,40 @@ +pub(crate) const COMPONENT_POISON_MSG: &str = "component mutex should not be poisoned"; + +macro_rules! component { + ($name:ident : $inner:ident { $($key:ident : $ty:ty = $value:expr,)* }) => { + #[derive(Clone)] + pub struct $name(::std::sync::Arc<($crate::session::SessionWeak, ::std::sync::Mutex<$inner>)>); + impl $name { + #[allow(dead_code)] + pub(crate) fn new(session: $crate::session::SessionWeak) -> $name { + debug!(target:"librespot::component", "new {}", stringify!($name)); + + $name(::std::sync::Arc::new((session, ::std::sync::Mutex::new($inner { + $($key : $value,)* + })))) + } + + #[allow(dead_code)] + fn lock R, R>(&self, f: F) -> R { + let mut inner = (self.0).1.lock() + .expect($crate::component::COMPONENT_POISON_MSG); + f(&mut inner) + } + + #[allow(dead_code)] + fn session(&self) -> $crate::session::Session { + (self.0).0.upgrade() + } + } + + struct $inner { + $($key : $ty,)* + } + + impl Drop for $inner { + fn drop(&mut self) { + debug!(target:"librespot::component", "drop {}", stringify!($name)); + } + } + } +} diff --git a/native/vendor/librespot-core/src/config.rs b/native/vendor/librespot-core/src/config.rs new file mode 100644 index 00000000..6c7a3484 --- /dev/null +++ b/native/vendor/librespot-core/src/config.rs @@ -0,0 +1,170 @@ +use std::{fmt, path::PathBuf, str::FromStr}; + +use librespot_protocol::devices::DeviceType as ProtoDeviceType; +use url::Url; + +pub(crate) const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd"; +pub(crate) const ANDROID_CLIENT_ID: &str = "9a8d2f0ce77a4e248bb71fefcb557637"; +pub(crate) const IOS_CLIENT_ID: &str = "58bd3c95768941ea9eb4350aaa033eb3"; + +// Sidetracks patch: the OAuth token comes from the keymaster/desktop client id, and +// Login5/client-token requests bind to the client id implied by this platform string. +// Treating the Android build as desktop keeps those requests consistent with the token. +pub const OS: &str = "linux"; + +// valid versions for some os: +// 'android': 30 +// 'ios': 17 +/// See [sysinfo::System::os_version] +pub fn os_version() -> String { + sysinfo::System::os_version().unwrap_or("0".into()) +} + +#[derive(Clone, Debug)] +pub struct SessionConfig { + pub client_id: String, + pub device_id: String, + pub proxy: Option, + pub ap_port: Option, + pub tmp_dir: PathBuf, + pub autoplay: Option, +} + +impl SessionConfig { + pub(crate) fn default_for_os(os: &str) -> Self { + let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string(); + let client_id = match os { + "android" => ANDROID_CLIENT_ID, + "ios" => IOS_CLIENT_ID, + _ => KEYMASTER_CLIENT_ID, + } + .to_owned(); + + Self { + client_id, + device_id, + proxy: None, + ap_port: None, + tmp_dir: std::env::temp_dir(), + autoplay: None, + } + } +} + +impl Default for SessionConfig { + fn default() -> Self { + Self::default_for_os(OS) + } +} + +#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq, Default)] +pub enum DeviceType { + Unknown = 0, + Computer = 1, + Tablet = 2, + Smartphone = 3, + #[default] + Speaker = 4, + Tv = 5, + Avr = 6, + Stb = 7, + AudioDongle = 8, + GameConsole = 9, + CastAudio = 10, + CastVideo = 11, + Automobile = 12, + Smartwatch = 13, + Chromebook = 14, + UnknownSpotify = 100, + CarThing = 101, + Observer = 102, +} + +impl FromStr for DeviceType { + type Err = (); + fn from_str(s: &str) -> Result { + use self::DeviceType::*; + match s.to_lowercase().as_ref() { + "computer" => Ok(Computer), + "tablet" => Ok(Tablet), + "smartphone" => Ok(Smartphone), + "speaker" => Ok(Speaker), + "tv" => Ok(Tv), + "avr" => Ok(Avr), + "stb" => Ok(Stb), + "audiodongle" => Ok(AudioDongle), + "gameconsole" => Ok(GameConsole), + "castaudio" => Ok(CastAudio), + "castvideo" => Ok(CastVideo), + "automobile" => Ok(Automobile), + "smartwatch" => Ok(Smartwatch), + "chromebook" => Ok(Chromebook), + "carthing" => Ok(CarThing), + _ => Err(()), + } + } +} + +impl From<&DeviceType> for &str { + fn from(d: &DeviceType) -> &'static str { + use self::DeviceType::*; + match d { + Unknown => "Unknown", + Computer => "Computer", + Tablet => "Tablet", + Smartphone => "Smartphone", + Speaker => "Speaker", + Tv => "TV", + Avr => "AVR", + Stb => "STB", + AudioDongle => "AudioDongle", + GameConsole => "GameConsole", + CastAudio => "CastAudio", + CastVideo => "CastVideo", + Automobile => "Automobile", + Smartwatch => "Smartwatch", + Chromebook => "Chromebook", + UnknownSpotify => "UnknownSpotify", + CarThing => "CarThing", + Observer => "Observer", + } + } +} + +impl From for &str { + fn from(d: DeviceType) -> &'static str { + (&d).into() + } +} + +impl fmt::Display for DeviceType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let str: &str = self.into(); + f.write_str(str) + } +} + +impl From for ProtoDeviceType { + fn from(value: DeviceType) -> Self { + match value { + DeviceType::Unknown => ProtoDeviceType::UNKNOWN, + DeviceType::Computer => ProtoDeviceType::COMPUTER, + DeviceType::Tablet => ProtoDeviceType::TABLET, + DeviceType::Smartphone => ProtoDeviceType::SMARTPHONE, + DeviceType::Speaker => ProtoDeviceType::SPEAKER, + DeviceType::Tv => ProtoDeviceType::TV, + DeviceType::Avr => ProtoDeviceType::AVR, + DeviceType::Stb => ProtoDeviceType::STB, + DeviceType::AudioDongle => ProtoDeviceType::AUDIO_DONGLE, + DeviceType::GameConsole => ProtoDeviceType::GAME_CONSOLE, + DeviceType::CastAudio => ProtoDeviceType::CAST_VIDEO, + DeviceType::CastVideo => ProtoDeviceType::CAST_AUDIO, + DeviceType::Automobile => ProtoDeviceType::AUTOMOBILE, + DeviceType::Smartwatch => ProtoDeviceType::SMARTWATCH, + DeviceType::Chromebook => ProtoDeviceType::CHROMEBOOK, + DeviceType::UnknownSpotify => ProtoDeviceType::UNKNOWN_SPOTIFY, + DeviceType::CarThing => ProtoDeviceType::CAR_THING, + DeviceType::Observer => ProtoDeviceType::OBSERVER, + } + } +} diff --git a/native/vendor/librespot-core/src/connection/codec.rs b/native/vendor/librespot-core/src/connection/codec.rs new file mode 100644 index 00000000..826839c6 --- /dev/null +++ b/native/vendor/librespot-core/src/connection/codec.rs @@ -0,0 +1,111 @@ +use std::io; + +use byteorder::{BigEndian, ByteOrder}; +use bytes::{BufMut, Bytes, BytesMut}; +use shannon::Shannon; +use thiserror::Error; +use tokio_util::codec::{Decoder, Encoder}; + +const HEADER_SIZE: usize = 3; +const MAC_SIZE: usize = 4; + +#[derive(Debug, Error)] +pub enum ApCodecError { + #[error("payload was malformed")] + Payload, +} + +#[derive(Debug)] +enum DecodeState { + Header, + Payload(u8, usize), +} + +pub struct ApCodec { + encode_nonce: u32, + encode_cipher: Shannon, + + decode_nonce: u32, + decode_cipher: Shannon, + decode_state: DecodeState, +} + +impl ApCodec { + pub fn new(send_key: &[u8], recv_key: &[u8]) -> ApCodec { + ApCodec { + encode_nonce: 0, + encode_cipher: Shannon::new(send_key), + + decode_nonce: 0, + decode_cipher: Shannon::new(recv_key), + decode_state: DecodeState::Header, + } + } +} + +impl Encoder<(u8, Vec)> for ApCodec { + type Error = io::Error; + + fn encode(&mut self, item: (u8, Vec), buf: &mut BytesMut) -> io::Result<()> { + let (cmd, payload) = item; + let offset = buf.len(); + + buf.reserve(3 + payload.len()); + buf.put_u8(cmd); + buf.put_u16(payload.len() as u16); + buf.extend_from_slice(&payload); + + self.encode_cipher.nonce_u32(self.encode_nonce); + self.encode_nonce += 1; + + self.encode_cipher.encrypt(&mut buf[offset..]); + + let mut mac = [0u8; MAC_SIZE]; + self.encode_cipher.finish(&mut mac); + buf.extend_from_slice(&mac); + + Ok(()) + } +} + +impl Decoder for ApCodec { + type Item = (u8, Bytes); + type Error = io::Error; + + fn decode(&mut self, buf: &mut BytesMut) -> io::Result> { + if let DecodeState::Header = self.decode_state { + if buf.len() >= HEADER_SIZE { + let mut header = [0u8; HEADER_SIZE]; + header.copy_from_slice(buf.split_to(HEADER_SIZE).as_ref()); + + self.decode_cipher.nonce_u32(self.decode_nonce); + self.decode_nonce += 1; + + self.decode_cipher.decrypt(&mut header); + + let cmd = header[0]; + let size = BigEndian::read_u16(&header[1..]) as usize; + self.decode_state = DecodeState::Payload(cmd, size); + } + } + + if let DecodeState::Payload(cmd, size) = self.decode_state { + if buf.len() >= size + MAC_SIZE { + self.decode_state = DecodeState::Header; + + let mut payload = buf.split_to(size + MAC_SIZE); + + self.decode_cipher + .decrypt(payload.get_mut(..size).ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, ApCodecError::Payload) + })?); + let mac = payload.split_off(size); + self.decode_cipher.check_mac(mac.as_ref())?; + + return Ok(Some((cmd, payload.freeze()))); + } + } + + Ok(None) + } +} diff --git a/native/vendor/librespot-core/src/connection/handshake.rs b/native/vendor/librespot-core/src/connection/handshake.rs new file mode 100644 index 00000000..1dea18ac --- /dev/null +++ b/native/vendor/librespot-core/src/connection/handshake.rs @@ -0,0 +1,270 @@ +use std::{env::consts::ARCH, io}; + +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; +use hmac::{Hmac, Mac}; +use protobuf::Message; +use rand::RngCore; +use rsa::{BigUint, Pkcs1v15Sign, RsaPublicKey}; +use sha1::{Digest, Sha1}; +use thiserror::Error; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio_util::codec::{Decoder, Framed}; + +use super::codec::ApCodec; + +use crate::{diffie_hellman::DhLocalKeys, version}; + +use crate::protocol; +use crate::protocol::keyexchange::{ + APResponseMessage, ClientHello, ClientResponsePlaintext, Platform, ProductFlags, +}; + +const SERVER_KEY: [u8; 256] = [ + 0xac, 0xe0, 0x46, 0x0b, 0xff, 0xc2, 0x30, 0xaf, 0xf4, 0x6b, 0xfe, 0xc3, 0xbf, 0xbf, 0x86, 0x3d, + 0xa1, 0x91, 0xc6, 0xcc, 0x33, 0x6c, 0x93, 0xa1, 0x4f, 0xb3, 0xb0, 0x16, 0x12, 0xac, 0xac, 0x6a, + 0xf1, 0x80, 0xe7, 0xf6, 0x14, 0xd9, 0x42, 0x9d, 0xbe, 0x2e, 0x34, 0x66, 0x43, 0xe3, 0x62, 0xd2, + 0x32, 0x7a, 0x1a, 0x0d, 0x92, 0x3b, 0xae, 0xdd, 0x14, 0x02, 0xb1, 0x81, 0x55, 0x05, 0x61, 0x04, + 0xd5, 0x2c, 0x96, 0xa4, 0x4c, 0x1e, 0xcc, 0x02, 0x4a, 0xd4, 0xb2, 0x0c, 0x00, 0x1f, 0x17, 0xed, + 0xc2, 0x2f, 0xc4, 0x35, 0x21, 0xc8, 0xf0, 0xcb, 0xae, 0xd2, 0xad, 0xd7, 0x2b, 0x0f, 0x9d, 0xb3, + 0xc5, 0x32, 0x1a, 0x2a, 0xfe, 0x59, 0xf3, 0x5a, 0x0d, 0xac, 0x68, 0xf1, 0xfa, 0x62, 0x1e, 0xfb, + 0x2c, 0x8d, 0x0c, 0xb7, 0x39, 0x2d, 0x92, 0x47, 0xe3, 0xd7, 0x35, 0x1a, 0x6d, 0xbd, 0x24, 0xc2, + 0xae, 0x25, 0x5b, 0x88, 0xff, 0xab, 0x73, 0x29, 0x8a, 0x0b, 0xcc, 0xcd, 0x0c, 0x58, 0x67, 0x31, + 0x89, 0xe8, 0xbd, 0x34, 0x80, 0x78, 0x4a, 0x5f, 0xc9, 0x6b, 0x89, 0x9d, 0x95, 0x6b, 0xfc, 0x86, + 0xd7, 0x4f, 0x33, 0xa6, 0x78, 0x17, 0x96, 0xc9, 0xc3, 0x2d, 0x0d, 0x32, 0xa5, 0xab, 0xcd, 0x05, + 0x27, 0xe2, 0xf7, 0x10, 0xa3, 0x96, 0x13, 0xc4, 0x2f, 0x99, 0xc0, 0x27, 0xbf, 0xed, 0x04, 0x9c, + 0x3c, 0x27, 0x58, 0x04, 0xb6, 0xb2, 0x19, 0xf9, 0xc1, 0x2f, 0x02, 0xe9, 0x48, 0x63, 0xec, 0xa1, + 0xb6, 0x42, 0xa0, 0x9d, 0x48, 0x25, 0xf8, 0xb3, 0x9d, 0xd0, 0xe8, 0x6a, 0xf9, 0x48, 0x4d, 0xa1, + 0xc2, 0xba, 0x86, 0x30, 0x42, 0xea, 0x9d, 0xb3, 0x08, 0x6c, 0x19, 0x0e, 0x48, 0xb3, 0x9d, 0x66, + 0xeb, 0x00, 0x06, 0xa2, 0x5a, 0xee, 0xa1, 0x1b, 0x13, 0x87, 0x3c, 0xd7, 0x19, 0xe6, 0x55, 0xbd, +]; + +#[derive(Debug, Error)] +pub enum HandshakeError { + #[error("invalid key length")] + InvalidLength, + #[error("server key verification failed")] + VerificationFailed, +} + +pub async fn handshake( + mut connection: T, +) -> io::Result> { + let local_keys = DhLocalKeys::random(&mut rand::rng()); + let gc = local_keys.public_key(); + let mut accumulator = client_hello(&mut connection, gc).await?; + let message: APResponseMessage = recv_packet(&mut connection, &mut accumulator).await?; + let remote_key = message + .challenge + .get_or_default() + .login_crypto_challenge + .get_or_default() + .diffie_hellman + .get_or_default() + .gs() + .to_owned(); + let remote_signature = message + .challenge + .get_or_default() + .login_crypto_challenge + .get_or_default() + .diffie_hellman + .get_or_default() + .gs_signature() + .to_owned(); + + // Prevent man-in-the-middle attacks: check server signature + let n = BigUint::from_bytes_be(&SERVER_KEY); + let e = BigUint::new(vec![65537]); + let public_key = RsaPublicKey::new(n, e).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + HandshakeError::VerificationFailed, + ) + })?; + + let hash = Sha1::digest(&remote_key); + let padding = Pkcs1v15Sign::new::(); + public_key + .verify(padding, &hash, &remote_signature) + .map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + HandshakeError::VerificationFailed, + ) + })?; + + // OK to proceed + let shared_secret = local_keys.shared_secret(&remote_key); + let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator)?; + let codec = ApCodec::new(&send_key, &recv_key); + + client_response(&mut connection, challenge).await?; + + Ok(codec.framed(connection)) +} + +async fn client_hello(connection: &mut T, gc: Vec) -> io::Result> +where + T: AsyncWrite + Unpin, +{ + let mut client_nonce = vec![0; 0x10]; + rand::rng().fill_bytes(&mut client_nonce); + + let platform = match crate::config::OS { + "freebsd" | "netbsd" | "openbsd" => match ARCH { + "x86_64" => Platform::PLATFORM_FREEBSD_X86_64, + _ => Platform::PLATFORM_FREEBSD_X86, + }, + "ios" => match ARCH { + "aarch64" => Platform::PLATFORM_IPHONE_ARM64, + _ => Platform::PLATFORM_IPHONE_ARM, + }, + // Rather than sending `Platform::PLATFORM_ANDROID_ARM` for "android", + // we are spoofing "android" as "linux", as otherwise during Session::connect + // all APs will reject the client with TryAnotherAP, no matter the credentials + // used was obtained via OAuth using KEYMASTER or ANDROID's client ID or + // Login5Manager::login + "linux" | "android" => match ARCH { + "arm" | "aarch64" => Platform::PLATFORM_LINUX_ARM, + "blackfin" => Platform::PLATFORM_LINUX_BLACKFIN, + "mips" => Platform::PLATFORM_LINUX_MIPS, + "sh" => Platform::PLATFORM_LINUX_SH, + "x86_64" => Platform::PLATFORM_LINUX_X86_64, + _ => Platform::PLATFORM_LINUX_X86, + }, + "macos" => match ARCH { + "ppc" | "ppc64" => Platform::PLATFORM_OSX_PPC, + "x86_64" => Platform::PLATFORM_OSX_X86_64, + _ => Platform::PLATFORM_OSX_X86, + }, + "windows" => match ARCH { + "arm" | "aarch64" => Platform::PLATFORM_WINDOWS_CE_ARM, + "x86_64" => Platform::PLATFORM_WIN32_X86_64, + _ => Platform::PLATFORM_WIN32_X86, + }, + _ => Platform::PLATFORM_LINUX_X86, + }; + + #[cfg(debug_assertions)] + const PRODUCT_FLAGS: ProductFlags = ProductFlags::PRODUCT_FLAG_DEV_BUILD; + #[cfg(not(debug_assertions))] + const PRODUCT_FLAGS: ProductFlags = ProductFlags::PRODUCT_FLAG_NONE; + + let mut packet = ClientHello::new(); + packet + .build_info + .mut_or_insert_default() + // ProductInfo won't push autoplay and perhaps other settings + // when set to anything else than PRODUCT_CLIENT + .set_product(protocol::keyexchange::Product::PRODUCT_CLIENT); + packet + .build_info + .mut_or_insert_default() + .product_flags + .push(PRODUCT_FLAGS.into()); + packet + .build_info + .mut_or_insert_default() + .set_platform(platform); + packet + .build_info + .mut_or_insert_default() + .set_version(version::SPOTIFY_VERSION); + packet + .cryptosuites_supported + .push(protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_SHANNON.into()); + packet + .login_crypto_hello + .mut_or_insert_default() + .diffie_hellman + .mut_or_insert_default() + .set_gc(gc); + packet + .login_crypto_hello + .mut_or_insert_default() + .diffie_hellman + .mut_or_insert_default() + .set_server_keys_known(1); + packet.set_client_nonce(client_nonce); + packet.set_padding(vec![0x1e]); + + let mut buffer = vec![0, 4]; + let size = 2 + 4 + packet.compute_size(); + as WriteBytesExt>::write_u32::(&mut buffer, size.try_into().unwrap())?; + packet.write_to_vec(&mut buffer)?; + + connection.write_all(&buffer[..]).await?; + Ok(buffer) +} + +async fn client_response(connection: &mut T, challenge: Vec) -> io::Result<()> +where + T: AsyncWrite + Unpin, +{ + let mut packet = ClientResponsePlaintext::new(); + packet + .login_crypto_response + .mut_or_insert_default() + .diffie_hellman + .mut_or_insert_default() + .set_hmac(challenge); + + packet.pow_response.mut_or_insert_default(); + packet.crypto_response.mut_or_insert_default(); + + let mut buffer = vec![]; + let size = 4 + packet.compute_size(); + as WriteBytesExt>::write_u32::(&mut buffer, size.try_into().unwrap())?; + packet.write_to_vec(&mut buffer)?; + + connection.write_all(&buffer[..]).await?; + Ok(()) +} + +async fn recv_packet(connection: &mut T, acc: &mut Vec) -> io::Result +where + T: AsyncRead + Unpin, + M: Message, +{ + let header = read_into_accumulator(connection, 4, acc).await?; + let size = BigEndian::read_u32(header) as usize; + let data = read_into_accumulator(connection, size - 4, acc).await?; + let message = M::parse_from_bytes(data)?; + Ok(message) +} + +async fn read_into_accumulator<'b, T: AsyncRead + Unpin>( + connection: &mut T, + size: usize, + acc: &'b mut Vec, +) -> io::Result<&'b mut [u8]> { + let offset = acc.len(); + acc.resize(offset + size, 0); + + connection.read_exact(&mut acc[offset..]).await?; + Ok(&mut acc[offset..]) +} + +fn compute_keys(shared_secret: &[u8], packets: &[u8]) -> io::Result<(Vec, Vec, Vec)> { + type HmacSha1 = Hmac; + + let mut data = Vec::with_capacity(0x64); + for i in 1..6 { + let mut mac = HmacSha1::new_from_slice(shared_secret).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidData, HandshakeError::InvalidLength) + })?; + mac.update(packets); + mac.update(&[i]); + data.extend_from_slice(&mac.finalize().into_bytes()); + } + + let mut mac = HmacSha1::new_from_slice(&data[..0x14]) + .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, HandshakeError::InvalidLength))?; + mac.update(packets); + + Ok(( + mac.finalize().into_bytes().to_vec(), + data[0x14..0x34].to_vec(), + data[0x34..0x54].to_vec(), + )) +} diff --git a/native/vendor/librespot-core/src/connection/mod.rs b/native/vendor/librespot-core/src/connection/mod.rs new file mode 100644 index 00000000..83017189 --- /dev/null +++ b/native/vendor/librespot-core/src/connection/mod.rs @@ -0,0 +1,194 @@ +mod codec; +mod handshake; + +pub use self::{codec::ApCodec, handshake::handshake}; + +use std::{io, time::Duration}; + +use futures_util::{SinkExt, StreamExt}; +use num_traits::FromPrimitive; +use protobuf::Message; +use thiserror::Error; +use tokio::net::TcpStream; +use tokio_util::codec::Framed; +use url::Url; + +use crate::{Error, authentication::Credentials, packet::PacketType, version}; + +use crate::protocol::keyexchange::{APLoginFailed, ErrorCode}; + +pub type Transport = Framed; + +fn login_error_message(code: &ErrorCode) -> &'static str { + pub use ErrorCode::*; + match code { + ProtocolError => "Protocol error", + TryAnotherAP => "Try another access point", + BadConnectionId => "Bad connection ID", + TravelRestriction => "Travel restriction", + PremiumAccountRequired => "Premium account required", + BadCredentials => "Bad credentials", + CouldNotValidateCredentials => "Could not validate credentials", + AccountExists => "Account exists", + ExtraVerificationRequired => "Extra verification required", + InvalidAppKey => "Invalid app key", + ApplicationBanned => "Application banned", + } +} + +#[derive(Debug, Error)] +pub enum AuthenticationError { + #[error("Login failed with reason: {}", login_error_message(.0))] + LoginFailed(ErrorCode), + #[error("invalid packet {0}")] + Packet(u8), + #[error("transport returned no data")] + Transport, +} + +impl From for Error { + fn from(err: AuthenticationError) -> Self { + match err { + AuthenticationError::LoginFailed(_) => Error::permission_denied(err), + AuthenticationError::Packet(_) => Error::unimplemented(err), + AuthenticationError::Transport => Error::unavailable(err), + } + } +} + +impl From for AuthenticationError { + fn from(login_failure: APLoginFailed) -> Self { + Self::LoginFailed(login_failure.error_code()) + } +} + +pub async fn connect(host: &str, port: u16, proxy: Option<&Url>) -> io::Result { + const TIMEOUT: Duration = Duration::from_secs(5); + tokio::time::timeout(TIMEOUT, { + let socket = crate::socket::connect(host, port, proxy).await?; + debug!("Connection to AP established."); + handshake(socket) + }) + .await? +} + +pub async fn connect_with_retry( + host: &str, + port: u16, + proxy: Option<&Url>, + max_retries: u8, +) -> io::Result { + let mut num_retries = 0; + loop { + match connect(host, port, proxy).await { + Ok(f) => return Ok(f), + Err(e) => { + debug!("Connection to \"{host}:{port}\" failed: {e}"); + if num_retries < max_retries { + num_retries += 1; + debug!("Retry access point..."); + continue; + } + return Err(e); + } + } + } +} + +pub async fn authenticate( + transport: &mut Transport, + credentials: Credentials, + device_id: &str, +) -> Result { + use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; + + let cpu_family = match std::env::consts::ARCH { + "blackfin" => CpuFamily::CPU_BLACKFIN, + "arm" | "aarch64" => CpuFamily::CPU_ARM, + "ia64" => CpuFamily::CPU_IA64, + "mips" => CpuFamily::CPU_MIPS, + "ppc" => CpuFamily::CPU_PPC, + "ppc64" => CpuFamily::CPU_PPC_64, + "sh" => CpuFamily::CPU_SH, + "x86" => CpuFamily::CPU_X86, + "x86_64" => CpuFamily::CPU_X86_64, + _ => CpuFamily::CPU_UNKNOWN, + }; + + let os = match crate::config::OS { + "android" => Os::OS_ANDROID, + "freebsd" | "netbsd" | "openbsd" => Os::OS_FREEBSD, + "ios" => Os::OS_IPHONE, + "linux" => Os::OS_LINUX, + "macos" => Os::OS_OSX, + "windows" => Os::OS_WINDOWS, + _ => Os::OS_UNKNOWN, + }; + + let mut packet = ClientResponseEncrypted::new(); + if let Some(username) = credentials.username { + packet + .login_credentials + .mut_or_insert_default() + .set_username(username); + } + packet + .login_credentials + .mut_or_insert_default() + .set_typ(credentials.auth_type); + packet + .login_credentials + .mut_or_insert_default() + .set_auth_data(credentials.auth_data); + packet + .system_info + .mut_or_insert_default() + .set_cpu_family(cpu_family); + packet.system_info.mut_or_insert_default().set_os(os); + packet + .system_info + .mut_or_insert_default() + .set_system_information_string(format!( + "librespot-{}-{}", + version::SHA_SHORT, + version::BUILD_ID + )); + packet + .system_info + .mut_or_insert_default() + .set_device_id(device_id.to_string()); + packet.set_version_string(format!("librespot {}", version::SEMVER)); + + let cmd = PacketType::Login; + let data = packet.write_to_bytes()?; + + debug!("Authenticating with AP using {:?}", credentials.auth_type); + transport.send((cmd as u8, data)).await?; + let (cmd, data) = transport + .next() + .await + .ok_or(AuthenticationError::Transport)??; + let packet_type = FromPrimitive::from_u8(cmd); + let result = match packet_type { + Some(PacketType::APWelcome) => { + let welcome_data = APWelcome::parse_from_bytes(data.as_ref())?; + + let reusable_credentials = Credentials { + username: Some(welcome_data.canonical_username().to_owned()), + auth_type: welcome_data.reusable_auth_credentials_type(), + auth_data: welcome_data.reusable_auth_credentials().to_owned(), + }; + + Ok(reusable_credentials) + } + Some(PacketType::AuthFailure) => { + let error_data = APLoginFailed::parse_from_bytes(data.as_ref())?; + Err(error_data.into()) + } + _ => { + trace!("Did not expect {cmd:?} AES key packet with data {data:#?}"); + Err(AuthenticationError::Packet(cmd)) + } + }; + Ok(result?) +} diff --git a/native/vendor/librespot-core/src/date.rs b/native/vendor/librespot-core/src/date.rs new file mode 100644 index 00000000..e77cf5ef --- /dev/null +++ b/native/vendor/librespot-core/src/date.rs @@ -0,0 +1,81 @@ +use std::{fmt::Debug, ops::Deref}; + +use time::{ + Date as _Date, OffsetDateTime, PrimitiveDateTime, Time, error::ComponentRange, + format_description::well_known::Iso8601, +}; + +use crate::Error; + +use librespot_protocol as protocol; +use protocol::metadata::Date as DateMessage; + +impl From for Error { + fn from(err: ComponentRange) -> Self { + Error::out_of_range(err) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct Date(pub OffsetDateTime); + +impl Deref for Date { + type Target = OffsetDateTime; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Date { + pub fn as_timestamp_ms(&self) -> i64 { + (self.0.unix_timestamp_nanos() / 1_000_000) as i64 + } + + pub fn from_timestamp_ms(timestamp: i64) -> Result { + let date_time = OffsetDateTime::from_unix_timestamp_nanos(timestamp as i128 * 1_000_000)?; + Ok(Self(date_time)) + } + + pub fn as_utc(&self) -> OffsetDateTime { + self.0 + } + + pub fn from_utc(date_time: PrimitiveDateTime) -> Self { + Self(date_time.assume_utc()) + } + + pub fn now_utc() -> Self { + Self(OffsetDateTime::now_utc()) + } + + pub fn from_iso8601(input: &str) -> Result { + let date_time = OffsetDateTime::parse(input, &Iso8601::DEFAULT)?; + Ok(Self(date_time)) + } +} + +impl TryFrom<&DateMessage> for Date { + type Error = crate::Error; + fn try_from(msg: &DateMessage) -> Result { + // Some metadata contains a year, but no month. In that case just set January. + let month = if msg.has_month() { + msg.month() as u8 + } else { + 1 + }; + + // Having no day will work, but may be unexpected: it will imply the last day + // of the month before. So prevent that, and just set day 1. + let day = if msg.has_day() { msg.day() as u8 } else { 1 }; + + let date = _Date::from_calendar_date(msg.year(), month.try_into()?, day)?; + let time = Time::from_hms(msg.hour() as u8, msg.minute() as u8, 0)?; + Ok(Self::from_utc(PrimitiveDateTime::new(date, time))) + } +} + +impl From for Date { + fn from(datetime: OffsetDateTime) -> Self { + Self(datetime) + } +} diff --git a/native/vendor/librespot-core/src/dealer/manager.rs b/native/vendor/librespot-core/src/dealer/manager.rs new file mode 100644 index 00000000..98ea0265 --- /dev/null +++ b/native/vendor/librespot-core/src/dealer/manager.rs @@ -0,0 +1,174 @@ +use futures_core::Stream; +use futures_util::StreamExt; +use std::{pin::Pin, str::FromStr, sync::OnceLock}; +use thiserror::Error; +use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; +use url::Url; + +use super::{ + Builder, Dealer, GetUrlResult, Request, RequestHandler, Responder, Response, Subscription, + protocol::Message, +}; +use crate::{Error, Session}; + +component! { + DealerManager: DealerManagerInner { + builder: OnceLock = OnceLock::from(Builder::new()), + dealer: OnceLock = OnceLock::new(), + } +} + +pub type BoxedStream = Pin + Send>>; +pub type BoxedStreamResult = BoxedStream>; + +#[derive(Error, Debug)] +enum DealerError { + #[error("Builder wasn't available")] + BuilderNotAvailable, + #[error("Websocket couldn't be started because: {0}")] + LaunchFailure(Error), + #[error("Failed to set dealer")] + CouldNotSetDealer, +} + +impl From for Error { + fn from(err: DealerError) -> Self { + Error::failed_precondition(err) + } +} + +#[derive(Debug)] +pub enum Reply { + Success, + Failure, + Unanswered, +} + +pub type RequestReply = (Request, mpsc::UnboundedSender); +type RequestReceiver = mpsc::UnboundedReceiver; +type RequestSender = mpsc::UnboundedSender; + +struct DealerRequestHandler(RequestSender); + +impl DealerRequestHandler { + pub fn new() -> (Self, RequestReceiver) { + let (tx, rx) = mpsc::unbounded_channel(); + (DealerRequestHandler(tx), rx) + } +} + +impl RequestHandler for DealerRequestHandler { + fn handle_request(&self, request: Request, responder: Responder) { + let (tx, mut rx) = mpsc::unbounded_channel(); + + if let Err(why) = self.0.send((request, tx)) { + error!("failed sending dealer request {why}"); + responder.send(Response { success: false }); + return; + } + + tokio::spawn(async move { + let reply = rx.recv().await.unwrap_or(Reply::Failure); + debug!("replying to ws request: {reply:?}"); + match reply { + Reply::Unanswered => responder.force_unanswered(), + Reply::Success | Reply::Failure => responder.send(Response { + success: matches!(reply, Reply::Success), + }), + } + }); + } +} + +impl DealerManager { + async fn get_url(session: Session) -> GetUrlResult { + let (host, port) = session.apresolver().resolve("dealer").await?; + let token = session.login5().auth_token().await?.access_token; + let url = format!("wss://{host}:{port}/?access_token={token}"); + let url = Url::from_str(&url)?; + Ok(url) + } + + pub fn add_listen_for(&self, url: impl Into) -> Result { + let url = url.into(); + self.lock(|inner| { + if let Some(dealer) = inner.dealer.get() { + dealer.subscribe(&[&url]) + } else if let Some(builder) = inner.builder.get_mut() { + builder.subscribe(&[&url]) + } else { + Err(DealerError::BuilderNotAvailable.into()) + } + }) + } + + pub fn listen_for( + &self, + uri: impl Into, + t: impl Fn(Message) -> Result + Send + 'static, + ) -> Result, Error> { + Ok(Box::pin(self.add_listen_for(uri)?.map(t))) + } + + pub fn add_handle_for(&self, url: impl Into) -> Result { + let url = url.into(); + + let (handler, receiver) = DealerRequestHandler::new(); + self.lock(|inner| { + if let Some(dealer) = inner.dealer.get() { + dealer.add_handler(&url, handler).map(|_| receiver) + } else if let Some(builder) = inner.builder.get_mut() { + builder.add_handler(&url, handler).map(|_| receiver) + } else { + Err(DealerError::BuilderNotAvailable.into()) + } + }) + } + + pub fn handle_for(&self, uri: impl Into) -> Result, Error> { + Ok(Box::pin( + self.add_handle_for(uri).map(UnboundedReceiverStream::new)?, + )) + } + + pub fn handles(&self, uri: &str) -> bool { + self.lock(|inner| { + if let Some(dealer) = inner.dealer.get() { + dealer.handles(uri) + } else if let Some(builder) = inner.builder.get() { + builder.handles(uri) + } else { + false + } + }) + } + + pub async fn start(&self) -> Result<(), Error> { + debug!("Launching dealer"); + + let session = self.session(); + // the url has to be a function that can retrieve a new url, + // otherwise when we later try to reconnect with the initial url/token + // and the token is expired we will just get 401 error + let get_url = move || Self::get_url(session.clone()); + + let dealer = self + .lock(move |inner| inner.builder.take()) + .ok_or(DealerError::BuilderNotAvailable)? + .launch(get_url, None) + .await + .map_err(DealerError::LaunchFailure)?; + + self.lock(|inner| inner.dealer.set(dealer)) + .map_err(|_| DealerError::CouldNotSetDealer)?; + + Ok(()) + } + + pub async fn close(&self) { + if let Some(dealer) = self.lock(|inner| inner.dealer.take()) { + dealer.close().await + } + } +} diff --git a/native/vendor/librespot-core/src/dealer/maps.rs b/native/vendor/librespot-core/src/dealer/maps.rs new file mode 100644 index 00000000..23d21a11 --- /dev/null +++ b/native/vendor/librespot-core/src/dealer/maps.rs @@ -0,0 +1,155 @@ +use std::collections::HashMap; + +use crate::Error; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum HandlerMapError { + #[error("request was already handled")] + AlreadyHandled, +} + +impl From for Error { + fn from(err: HandlerMapError) -> Self { + Error::aborted(err) + } +} + +pub enum HandlerMap { + Leaf(T), + Branch(HashMap>), +} + +impl Default for HandlerMap { + fn default() -> Self { + Self::Branch(HashMap::new()) + } +} + +impl HandlerMap { + pub fn contains(&self, path: &str) -> bool { + matches!(self, HandlerMap::Branch(map) if map.contains_key(path)) + } + + pub fn insert<'a>( + &mut self, + mut path: impl Iterator, + handler: T, + ) -> Result<(), Error> { + match self { + Self::Leaf(_) => Err(HandlerMapError::AlreadyHandled.into()), + Self::Branch(children) => { + if let Some(component) = path.next() { + let node = children.entry(component.to_owned()).or_default(); + node.insert(path, handler) + } else if children.is_empty() { + *self = Self::Leaf(handler); + Ok(()) + } else { + Err(HandlerMapError::AlreadyHandled.into()) + } + } + } + } + + pub fn get<'a>(&self, mut path: impl Iterator) -> Option<&T> { + match self { + Self::Leaf(t) => Some(t), + Self::Branch(m) => { + let component = path.next()?; + m.get(component)?.get(path) + } + } + } + + pub fn remove<'a>(&mut self, mut path: impl Iterator) -> Option { + match self { + Self::Leaf(_) => match std::mem::take(self) { + Self::Leaf(t) => Some(t), + _ => unreachable!(), + }, + Self::Branch(map) => { + let component = path.next()?; + let next = map.get_mut(component)?; + let result = next.remove(path); + match &*next { + Self::Branch(b) if b.is_empty() => { + map.remove(component); + } + _ => (), + } + result + } + } + } +} + +pub struct SubscriberMap { + subscribed: Vec, + children: HashMap>, +} + +impl Default for SubscriberMap { + fn default() -> Self { + Self { + subscribed: Vec::new(), + children: HashMap::new(), + } + } +} + +impl SubscriberMap { + pub fn insert<'a>(&mut self, mut path: impl Iterator, handler: T) { + if let Some(component) = path.next() { + self.children + .entry(component.to_owned()) + .or_default() + .insert(path, handler); + } else { + self.subscribed.push(handler); + } + } + + pub fn contains<'a>(&self, mut path: impl Iterator) -> bool { + if !self.subscribed.is_empty() { + return true; + } + + if let Some(next) = path.next() { + if let Some(next_map) = self.children.get(next) { + return next_map.contains(path); + } + } else { + return !self.is_empty(); + } + + false + } + + pub fn is_empty(&self) -> bool { + self.children.is_empty() && self.subscribed.is_empty() + } + + pub fn retain<'a>( + &mut self, + mut path: impl Iterator, + fun: &mut impl FnMut(&T) -> bool, + ) -> bool { + let mut handled_by_any = false; + self.subscribed.retain(|x| { + handled_by_any = true; + fun(x) + }); + + if let Some(next) = path.next() { + if let Some(y) = self.children.get_mut(next) { + handled_by_any = handled_by_any || y.retain(path, fun); + if y.is_empty() { + self.children.remove(next); + } + } + } + + handled_by_any + } +} diff --git a/native/vendor/librespot-core/src/dealer/mod.rs b/native/vendor/librespot-core/src/dealer/mod.rs new file mode 100644 index 00000000..63ee6e72 --- /dev/null +++ b/native/vendor/librespot-core/src/dealer/mod.rs @@ -0,0 +1,724 @@ +pub mod manager; +mod maps; +pub mod protocol; + +use std::{ + iter, + pin::Pin, + sync::{ + Arc, Mutex, + atomic::{self, AtomicBool}, + }, + task::Poll, + time::Duration, +}; + +use futures_core::{Future, Stream}; +use futures_util::{SinkExt, StreamExt, future::join_all}; +use thiserror::Error; +use tokio::{ + select, + sync::{ + Semaphore, + mpsc::{self, UnboundedReceiver}, + }, + task::JoinHandle, +}; +use tokio_tungstenite::tungstenite; +use tungstenite::error::UrlError; +use url::Url; + +use self::{ + maps::*, + protocol::{Message, MessageOrRequest, Request, WebsocketMessage, WebsocketRequest}, +}; + +use crate::{ + Error, socket, + util::{CancelOnDrop, TimeoutOnDrop, keep_flushing}, +}; + +type WsMessage = tungstenite::Message; +type WsError = tungstenite::Error; +type WsResult = Result; +type GetUrlResult = Result; + +impl From for Error { + fn from(err: WsError) -> Self { + Error::failed_precondition(err) + } +} + +const WEBSOCKET_CLOSE_TIMEOUT: Duration = Duration::from_secs(3); + +const PING_INTERVAL: Duration = Duration::from_secs(30); +const PING_TIMEOUT: Duration = Duration::from_secs(3); + +const RECONNECT_INTERVAL: Duration = Duration::from_secs(10); + +const DEALER_REQUEST_HANDLERS_POISON_MSG: &str = + "dealer request handlers mutex should not be poisoned"; +const DEALER_MESSAGE_HANDLERS_POISON_MSG: &str = + "dealer message handlers mutex should not be poisoned"; + +struct Response { + pub success: bool, +} + +struct Responder { + key: String, + tx: mpsc::UnboundedSender, + sent: bool, +} + +impl Responder { + fn new(key: String, tx: mpsc::UnboundedSender) -> Self { + Self { + key, + tx, + sent: false, + } + } + + // Should only be called once + fn send_internal(&mut self, response: Response) { + let response = serde_json::json!({ + "type": "reply", + "key": &self.key, + "payload": { + "success": response.success, + } + }) + .to_string(); + + if let Err(e) = self.tx.send(WsMessage::Text(response.into())) { + warn!("Wasn't able to reply to dealer request: {e}"); + } + } + + pub fn send(mut self, response: Response) { + self.send_internal(response); + self.sent = true; + } + + pub fn force_unanswered(mut self) { + self.sent = true; + } +} + +impl Drop for Responder { + fn drop(&mut self) { + if !self.sent { + self.send_internal(Response { success: false }); + } + } +} + +trait IntoResponse { + fn respond(self, responder: Responder); +} + +impl IntoResponse for Response { + fn respond(self, responder: Responder) { + responder.send(self) + } +} + +impl IntoResponse for F +where + F: Future + Send + 'static, +{ + fn respond(self, responder: Responder) { + tokio::spawn(async move { + responder.send(self.await); + }); + } +} + +impl RequestHandler for F +where + F: (Fn(Request) -> R) + Send + 'static, + R: IntoResponse, +{ + fn handle_request(&self, request: Request, responder: Responder) { + self(request).respond(responder); + } +} + +trait RequestHandler: Send + 'static { + fn handle_request(&self, request: Request, responder: Responder); +} + +type MessageHandler = mpsc::UnboundedSender; + +// TODO: Maybe it's possible to unregister subscription directly when they +// are dropped instead of on next failed attempt. +pub struct Subscription(UnboundedReceiver); + +impl Stream for Subscription { + type Item = Message; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + self.0.poll_recv(cx) + } +} + +fn split_uri(s: &str) -> Option> { + let (scheme, sep, rest) = if let Some(rest) = s.strip_prefix("hm://") { + ("hm", '/', rest) + } else if let Some(rest) = s.strip_prefix("spotify:") { + ("spotify", ':', rest) + } else if s.contains('/') { + ("", '/', s) + } else { + return None; + }; + + let rest = rest.trim_end_matches(sep); + let split = rest.split(sep); + + Some(iter::once(scheme).chain(split)) +} + +#[derive(Debug, Clone, Error)] +enum AddHandlerError { + #[error("There is already a handler for the given uri")] + AlreadyHandled, + #[error("The specified uri {0} is invalid")] + InvalidUri(String), +} + +impl From for Error { + fn from(err: AddHandlerError) -> Self { + match err { + AddHandlerError::AlreadyHandled => Error::aborted(err), + AddHandlerError::InvalidUri(_) => Error::invalid_argument(err), + } + } +} + +#[derive(Debug, Clone, Error)] +enum SubscriptionError { + #[error("The specified uri is invalid")] + InvalidUri(String), +} + +impl From for Error { + fn from(err: SubscriptionError) -> Self { + Error::invalid_argument(err) + } +} + +fn add_handler( + map: &mut HandlerMap>, + uri: &str, + handler: impl RequestHandler, +) -> Result<(), Error> { + let split = split_uri(uri).ok_or_else(|| AddHandlerError::InvalidUri(uri.to_string()))?; + map.insert(split, Box::new(handler)) +} + +fn remove_handler(map: &mut HandlerMap, uri: &str) -> Option { + map.remove(split_uri(uri)?) +} + +fn subscribe( + map: &mut SubscriberMap, + uris: &[&str], +) -> Result { + let (tx, rx) = mpsc::unbounded_channel(); + + for &uri in uris { + let split = split_uri(uri).ok_or_else(|| SubscriptionError::InvalidUri(uri.to_string()))?; + map.insert(split, tx.clone()); + } + + Ok(Subscription(rx)) +} + +fn handles( + req_map: &HandlerMap>, + msg_map: &SubscriberMap, + uri: &str, +) -> bool { + if req_map.contains(uri) { + return true; + } + + match split_uri(uri) { + None => false, + Some(mut split) => msg_map.contains(&mut split), + } +} + +#[derive(Default)] +struct Builder { + message_handlers: SubscriberMap, + request_handlers: HandlerMap>, +} + +macro_rules! create_dealer { + ($builder:expr, $shared:ident -> $body:expr) => { + match $builder { + builder => { + let shared = Arc::new(DealerShared { + message_handlers: Mutex::new(builder.message_handlers), + request_handlers: Mutex::new(builder.request_handlers), + notify_drop: Semaphore::new(0), + }); + + let handle = { + let $shared = Arc::clone(&shared); + tokio::spawn($body) + }; + + Dealer { + shared, + handle: TimeoutOnDrop::new(handle, WEBSOCKET_CLOSE_TIMEOUT), + } + } + } + }; +} + +impl Builder { + pub fn new() -> Self { + Self::default() + } + + pub fn add_handler(&mut self, uri: &str, handler: impl RequestHandler) -> Result<(), Error> { + add_handler(&mut self.request_handlers, uri, handler) + } + + pub fn subscribe(&mut self, uris: &[&str]) -> Result { + subscribe(&mut self.message_handlers, uris) + } + + pub fn handles(&self, uri: &str) -> bool { + handles(&self.request_handlers, &self.message_handlers, uri) + } + + pub fn launch_in_background(self, get_url: F, proxy: Option) -> Dealer + where + Fut: Future + Send + 'static, + F: (Fn() -> Fut) + Send + 'static, + { + create_dealer!(self, shared -> run(shared, None, get_url, proxy)) + } + + pub async fn launch(self, get_url: F, proxy: Option) -> WsResult + where + Fut: Future + Send + 'static, + F: (Fn() -> Fut) + Send + 'static, + { + let dealer = create_dealer!(self, shared -> { + // Try to connect. + let url = get_url().await?; + let tasks = connect(&url, proxy.as_ref(), &shared).await?; + + // If a connection is established, continue in a background task. + run(shared, Some(tasks), get_url, proxy) + }); + + Ok(dealer) + } +} + +struct DealerShared { + message_handlers: Mutex>, + request_handlers: Mutex>>, + + // Semaphore with 0 permits. By closing this semaphore, we indicate + // that the actual Dealer struct has been dropped. + notify_drop: Semaphore, +} + +impl DealerShared { + fn dispatch_message(&self, mut msg: WebsocketMessage) { + let msg = match msg.handle_payload() { + Ok(value) => Message { + headers: msg.headers, + payload: value, + uri: msg.uri, + }, + Err(why) => { + warn!("failure during data parsing for {}: {why}", msg.uri); + return; + } + }; + + if let Some(split) = split_uri(&msg.uri) { + if self + .message_handlers + .lock() + .expect(DEALER_MESSAGE_HANDLERS_POISON_MSG) + .retain(split, &mut |tx| tx.send(msg.clone()).is_ok()) + { + return; + } + } + + debug!("No subscriber for msg.uri: {}", msg.uri); + } + + fn dispatch_request( + &self, + request: WebsocketRequest, + send_tx: &mpsc::UnboundedSender, + ) { + trace!("dealer request {}", &request.message_ident); + + let payload_request = match request.handle_payload() { + Ok(payload) => payload, + Err(why) => { + warn!("request payload handling failed because of {why}"); + return; + } + }; + + // ResponseSender will automatically send "success: false" if it is dropped without an answer. + let responder = Responder::new(request.key.clone(), send_tx.clone()); + + let split = if let Some(split) = split_uri(&request.message_ident) { + split + } else { + warn!( + "Dealer request with invalid message_ident: {}", + &request.message_ident + ); + return; + }; + + let handler_map = self + .request_handlers + .lock() + .expect(DEALER_REQUEST_HANDLERS_POISON_MSG); + + if let Some(handler) = handler_map.get(split) { + handler.handle_request(payload_request, responder); + return; + } + + warn!("No handler for message_ident: {}", &request.message_ident); + } + + fn dispatch(&self, m: MessageOrRequest, send_tx: &mpsc::UnboundedSender) { + match m { + MessageOrRequest::Message(m) => self.dispatch_message(m), + MessageOrRequest::Request(r) => self.dispatch_request(r, send_tx), + } + } + + async fn closed(&self) { + if self.notify_drop.acquire().await.is_ok() { + error!("should never have gotten a permit"); + } + } + + fn is_closed(&self) -> bool { + self.notify_drop.is_closed() + } +} + +struct Dealer { + shared: Arc, + handle: TimeoutOnDrop>, +} + +impl Dealer { + pub fn add_handler(&self, uri: &str, handler: H) -> Result<(), Error> + where + H: RequestHandler, + { + add_handler( + &mut self + .shared + .request_handlers + .lock() + .expect(DEALER_REQUEST_HANDLERS_POISON_MSG), + uri, + handler, + ) + } + + pub fn remove_handler(&self, uri: &str) -> Option> { + remove_handler( + &mut self + .shared + .request_handlers + .lock() + .expect(DEALER_REQUEST_HANDLERS_POISON_MSG), + uri, + ) + } + + pub fn subscribe(&self, uris: &[&str]) -> Result { + subscribe( + &mut self + .shared + .message_handlers + .lock() + .expect(DEALER_MESSAGE_HANDLERS_POISON_MSG), + uris, + ) + } + + pub fn handles(&self, uri: &str) -> bool { + handles( + &self + .shared + .request_handlers + .lock() + .expect(DEALER_REQUEST_HANDLERS_POISON_MSG), + &self + .shared + .message_handlers + .lock() + .expect(DEALER_MESSAGE_HANDLERS_POISON_MSG), + uri, + ) + } + + pub async fn close(mut self) { + debug!("closing dealer"); + + self.shared.notify_drop.close(); + + if let Some(handle) = self.handle.take() { + if let Err(e) = CancelOnDrop(handle).await { + error!("error aborting dealer operations: {e}"); + } + } + } +} + +/// Initializes a connection and returns futures that will finish when the connection is closed/lost. +async fn connect( + address: &Url, + proxy: Option<&Url>, + shared: &Arc, +) -> WsResult<(JoinHandle<()>, JoinHandle<()>)> { + let host = address + .host_str() + .ok_or(WsError::Url(UrlError::NoHostName))?; + + let default_port = match address.scheme() { + "ws" => 80, + "wss" => 443, + _ => return Err(WsError::Url(UrlError::UnsupportedUrlScheme).into()), + }; + + let port = address.port().unwrap_or(default_port); + + let stream = socket::connect(host, port, proxy).await?; + + let (mut ws_tx, ws_rx) = tokio_tungstenite::client_async_tls(address.as_str(), stream) + .await? + .0 + .split(); + + let (send_tx, mut send_rx) = mpsc::unbounded_channel::(); + + // Spawn a task that will forward messages from the channel to the websocket. + let send_task = { + let shared = Arc::clone(shared); + + tokio::spawn(async move { + let result = loop { + select! { + biased; + () = shared.closed() => { + break Ok(None); + } + msg = send_rx.recv() => { + if let Some(msg) = msg { + // New message arrived through channel + if let WsMessage::Close(close_frame) = msg { + break Ok(close_frame); + } + + if let Err(e) = ws_tx.feed(msg).await { + break Err(e); + } + } else { + break Ok(None); + } + }, + e = keep_flushing(&mut ws_tx) => { + break Err(e) + } + else => (), + } + }; + + send_rx.close(); + + // I don't trust in tokio_tungstenite's implementation of Sink::close. + let result = match result { + Ok(close_frame) => ws_tx.send(WsMessage::Close(close_frame)).await, + Err(WsError::AlreadyClosed) | Err(WsError::ConnectionClosed) => ws_tx.flush().await, + Err(e) => { + warn!("Dealer finished with an error: {e}"); + ws_tx.send(WsMessage::Close(None)).await + } + }; + + if let Err(e) = result { + warn!("Error while closing websocket: {e}"); + } + + debug!("Dropping send task"); + }) + }; + + let shared = Arc::clone(shared); + + // A task that receives messages from the web socket. + let receive_task = tokio::spawn(async { + let pong_received = AtomicBool::new(true); + let send_tx = send_tx; + let shared = shared; + + let receive_task = async { + let mut ws_rx = ws_rx; + + loop { + match ws_rx.next().await { + Some(Ok(msg)) => match msg { + WsMessage::Text(t) => match serde_json::from_str(&t) { + Ok(m) => shared.dispatch(m, &send_tx), + Err(e) => warn!("Message couldn't be parsed: {e}. Message was {t}"), + }, + WsMessage::Binary(_) => { + info!("Received invalid binary message"); + } + WsMessage::Pong(_) => { + trace!("Received pong"); + pong_received.store(true, atomic::Ordering::Relaxed); + } + _ => (), // tungstenite handles Close and Ping automatically + }, + Some(Err(e)) => { + warn!("Websocket connection failed: {e}"); + break; + } + None => { + debug!("Websocket connection closed."); + break; + } + } + } + }; + + // Sends pings and checks whether a pong comes back. + let ping_task = async { + use tokio::time::{interval, sleep}; + + let mut timer = interval(PING_INTERVAL); + + loop { + timer.tick().await; + + pong_received.store(false, atomic::Ordering::Relaxed); + if send_tx + .send(WsMessage::Ping(bytes::Bytes::default())) + .is_err() + { + // The sender is closed. + break; + } + + trace!("Sent ping"); + + sleep(PING_TIMEOUT).await; + + if !pong_received.load(atomic::Ordering::SeqCst) { + // No response + warn!("Websocket peer does not respond."); + break; + } + } + }; + + // Exit this task as soon as one our subtasks fails. + // In both cases the connection is probably lost. + select! { + () = ping_task => (), + () = receive_task => () + } + + // Try to take send_task down with us, in case it's still alive. + let _ = send_tx.send(WsMessage::Close(None)); + + debug!("Dropping receive task"); + }); + + Ok((send_task, receive_task)) +} + +/// The main background task for `Dealer`, which coordinates reconnecting. +async fn run( + shared: Arc, + initial_tasks: Option<(JoinHandle<()>, JoinHandle<()>)>, + mut get_url: F, + proxy: Option, +) -> Result<(), Error> +where + Fut: Future + Send + 'static, + F: (FnMut() -> Fut) + Send + 'static, +{ + let init_task = |t| Some(TimeoutOnDrop::new(t, WEBSOCKET_CLOSE_TIMEOUT)); + + let mut tasks = if let Some((s, r)) = initial_tasks { + (init_task(s), init_task(r)) + } else { + (None, None) + }; + + while !shared.is_closed() { + match &mut tasks { + (Some(t0), Some(t1)) => { + select! { + () = shared.closed() => break, + r = t0 => { + if let Err(e) = r { + error!("timeout on task 0: {e}"); + } + tasks.0.take(); + }, + r = t1 => { + if let Err(e) = r { + error!("timeout on task 1: {e}"); + } + tasks.1.take(); + } + } + } + _ => { + let url = select! { + () = shared.closed() => { + break + }, + e = get_url() => e + }?; + + match connect(&url, proxy.as_ref(), &shared).await { + Ok((s, r)) => tasks = (init_task(s), init_task(r)), + Err(e) => { + error!("Error while connecting: {e}"); + tokio::time::sleep(RECONNECT_INTERVAL).await; + } + } + } + } + } + + let tasks = tasks.0.into_iter().chain(tasks.1); + + let _ = join_all(tasks).await; + + Ok(()) +} diff --git a/native/vendor/librespot-core/src/dealer/protocol.rs b/native/vendor/librespot-core/src/dealer/protocol.rs new file mode 100644 index 00000000..803fe300 --- /dev/null +++ b/native/vendor/librespot-core/src/dealer/protocol.rs @@ -0,0 +1,196 @@ +pub mod request; + +pub use request::*; + +use std::collections::HashMap; +use std::io::{Error as IoError, Read}; + +use crate::{Error, deserialize_with::json_proto}; +use base64::{DecodeError, Engine, prelude::BASE64_STANDARD}; +use flate2::read::GzDecoder; +use log::LevelFilter; +use serde::Deserialize; +use serde_json::Error as SerdeError; +use thiserror::Error; + +const IGNORE_UNKNOWN: protobuf_json_mapping::ParseOptions = protobuf_json_mapping::ParseOptions { + ignore_unknown_fields: true, + _future_options: (), +}; + +type JsonValue = serde_json::Value; + +#[derive(Debug, Error)] +enum ProtocolError { + #[error("base64 decoding failed: {0}")] + Base64(DecodeError), + #[error("gzip decoding failed: {0}")] + GZip(IoError), + #[error("deserialization failed: {0}")] + Deserialization(SerdeError), + #[error("payload had more then one value. had {0} values")] + MoreThenOneValue(usize), + #[error("received unexpected data {0:#?}")] + UnexpectedData(PayloadValue), + #[error("payload was empty")] + Empty, +} + +impl From for Error { + fn from(err: ProtocolError) -> Self { + match err { + ProtocolError::UnexpectedData(_) => Error::unavailable(err), + _ => Error::failed_precondition(err), + } + } +} + +#[derive(Clone, Debug, Deserialize)] +pub(super) struct Payload { + pub compressed: String, +} + +#[derive(Clone, Debug, Deserialize)] +pub(super) struct WebsocketRequest { + #[serde(default)] + pub headers: HashMap, + pub message_ident: String, + pub key: String, + pub payload: Payload, +} + +#[derive(Clone, Debug, Deserialize)] +pub(super) struct WebsocketMessage { + #[serde(default)] + pub headers: HashMap, + pub method: Option, + #[serde(default)] + pub payloads: Vec, + pub uri: String, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum MessagePayloadValue { + String(String), + Bytes(Vec), + Json(JsonValue), +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub(super) enum MessageOrRequest { + Message(WebsocketMessage), + Request(WebsocketRequest), +} + +#[derive(Clone, Debug)] +pub enum PayloadValue { + Empty, + Raw(Vec), + Json(String), +} + +#[derive(Clone, Debug)] +pub struct Message { + pub headers: HashMap, + pub payload: PayloadValue, + pub uri: String, +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum FallbackWrapper { + Inner(#[serde(deserialize_with = "json_proto")] T), + Fallback(JsonValue), +} + +impl Message { + pub fn try_from_json( + value: Self, + ) -> Result, Error> { + match value.payload { + PayloadValue::Json(json) => Ok(serde_json::from_str(&json)?), + other => Err(ProtocolError::UnexpectedData(other).into()), + } + } + + pub fn from_raw(value: Self) -> Result { + match value.payload { + PayloadValue::Raw(bytes) => { + M::parse_from_bytes(&bytes).map_err(Error::failed_precondition) + } + other => Err(ProtocolError::UnexpectedData(other).into()), + } + } +} + +impl WebsocketMessage { + pub fn handle_payload(&mut self) -> Result { + if self.payloads.is_empty() { + return Ok(PayloadValue::Empty); + } else if self.payloads.len() > 1 { + return Err(ProtocolError::MoreThenOneValue(self.payloads.len()).into()); + } + + let payload = self.payloads.pop().ok_or(ProtocolError::Empty)?; + let bytes = match payload { + MessagePayloadValue::String(string) => BASE64_STANDARD + .decode(string) + .map_err(ProtocolError::Base64)?, + MessagePayloadValue::Bytes(bytes) => bytes, + MessagePayloadValue::Json(json) => return Ok(PayloadValue::Json(json.to_string())), + }; + + handle_transfer_encoding(&self.headers, bytes).map(PayloadValue::Raw) + } +} + +impl WebsocketRequest { + pub fn handle_payload(&self) -> Result { + let payload_bytes = BASE64_STANDARD + .decode(&self.payload.compressed) + .map_err(ProtocolError::Base64)?; + + let payload = handle_transfer_encoding(&self.headers, payload_bytes)?; + let payload = String::from_utf8(payload)?; + + if log::max_level() >= LevelFilter::Trace { + if let Ok(json) = serde_json::from_str::(&payload) { + trace!("websocket request: {json:#?}"); + } else { + trace!("websocket request: {payload}"); + } + } + + serde_json::from_str(&payload) + .map_err(ProtocolError::Deserialization) + .map_err(Into::into) + } +} + +fn handle_transfer_encoding( + headers: &HashMap, + data: Vec, +) -> Result, Error> { + let encoding = headers.get("Transfer-Encoding").map(String::as_str); + if let Some(encoding) = encoding { + trace!("message was sent with {encoding} encoding "); + } else { + trace!("message was sent with no encoding "); + } + + if !matches!(encoding, Some("gzip")) { + return Ok(data); + } + + let mut gz = GzDecoder::new(&data[..]); + let mut bytes = vec![]; + match gz.read_to_end(&mut bytes) { + Ok(i) if i == bytes.len() => Ok(bytes), + Ok(_) => Err(Error::failed_precondition( + "read bytes mismatched with expected bytes", + )), + Err(why) => Err(ProtocolError::GZip(why).into()), + } +} diff --git a/native/vendor/librespot-core/src/dealer/protocol/request.rs b/native/vendor/librespot-core/src/dealer/protocol/request.rs new file mode 100644 index 00000000..8125750c --- /dev/null +++ b/native/vendor/librespot-core/src/dealer/protocol/request.rs @@ -0,0 +1,217 @@ +use crate::{ + deserialize_with::*, + protocol::{ + context::Context, + context_player_options::ContextPlayerOptionOverrides, + player::{PlayOrigin, ProvidedTrack}, + transfer_state::TransferState, + }, +}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fmt::{Display, Formatter}; + +#[derive(Clone, Debug, Deserialize)] +pub struct Request { + pub message_id: u32, + // todo: did only send target_alias_id: null so far, maybe we just ignore it, will see + // pub target_alias_id: Option<()>, + pub sent_by_device_id: String, + pub command: Command, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "endpoint", rename_all = "snake_case")] +pub enum Command { + Transfer(TransferCommand), + #[serde(deserialize_with = "boxed")] + Play(Box), + Pause(PauseCommand), + SeekTo(SeekToCommand), + SetShufflingContext(SetValueCommand), + SetRepeatingTrack(SetValueCommand), + SetRepeatingContext(SetValueCommand), + AddToQueue(AddToQueueCommand), + SetQueue(SetQueueCommand), + SetOptions(SetOptionsCommand), + UpdateContext(UpdateContextCommand), + SkipNext(SkipNextCommand), + // commands that don't send any context (at least not usually...) + SkipPrev(GenericCommand), + Resume(GenericCommand), + // catch unknown commands, so that we can implement them later + #[serde(untagged)] + Unknown(Value), +} + +impl Display for Command { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + use Command::*; + + write!( + f, + "endpoint: {}{}", + matches!(self, Unknown(_)) + .then_some("unknown ") + .unwrap_or_default(), + match self { + Transfer(_) => "transfer", + Play(_) => "play", + Pause(_) => "pause", + SeekTo(_) => "seek_to", + SetShufflingContext(_) => "set_shuffling_context", + SetRepeatingContext(_) => "set_repeating_context", + SetRepeatingTrack(_) => "set_repeating_track", + AddToQueue(_) => "add_to_queue", + SetQueue(_) => "set_queue", + SetOptions(_) => "set_options", + UpdateContext(_) => "update_context", + SkipNext(_) => "skip_next", + SkipPrev(_) => "skip_prev", + Resume(_) => "resume", + Unknown(json) => { + json.as_object() + .and_then(|obj| obj.get("endpoint").map(|v| v.as_str())) + .flatten() + .unwrap_or("???") + } + } + ) + } +} + +#[derive(Clone, Debug, Deserialize)] +pub struct TransferCommand { + #[serde(default, deserialize_with = "base64_proto")] + pub data: Option, + pub options: TransferOptions, + pub from_device_identifier: String, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct PlayCommand { + #[serde(deserialize_with = "json_proto")] + pub context: Context, + #[serde(deserialize_with = "json_proto")] + pub play_origin: PlayOrigin, + pub options: PlayOptions, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct PauseCommand { + // does send options with it, but seems to be empty, investigate which options are send here + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct SeekToCommand { + pub value: u32, + pub position: u32, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct SkipNextCommand { + #[serde(default, deserialize_with = "option_json_proto")] + pub track: Option, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct SetValueCommand { + pub value: bool, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct AddToQueueCommand { + #[serde(deserialize_with = "json_proto")] + pub track: ProvidedTrack, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct SetQueueCommand { + #[serde(deserialize_with = "vec_json_proto")] + pub next_tracks: Vec, + #[serde(deserialize_with = "vec_json_proto")] + pub prev_tracks: Vec, + // this queue revision is actually the last revision, so using it will not update the web ui + // might be that internally they use the last revision to create the next revision + pub queue_revision: String, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct SetOptionsCommand { + pub shuffling_context: Option, + pub repeating_context: Option, + pub repeating_track: Option, + pub options: Option, + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct UpdateContextCommand { + #[serde(deserialize_with = "json_proto")] + pub context: Context, + pub session_id: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct GenericCommand { + pub logging_params: LoggingParams, +} + +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct TransferOptions { + #[serde(skip_serializing_if = "Option::is_none")] + pub restore_paused: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub restore_position: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub restore_track: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub retain_session: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct PlayOptions { + pub skip_to: Option, + #[serde(default, deserialize_with = "option_json_proto")] + pub player_options_override: Option, + pub license: Option, + // possible to send wie web-api + pub seek_to: Option, + // mobile + pub always_play_something: Option, + pub audio_stream: Option, + pub initially_paused: Option, + pub prefetch_level: Option, + pub system_initiated: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct OptionsOptions { + only_for_local_device: bool, + override_restrictions: bool, + system_initiated: bool, +} + +#[derive(Clone, Debug, Deserialize, Default)] +pub struct SkipTo { + pub track_uid: Option, + pub track_uri: Option, + pub track_index: Option, +} + +#[derive(Clone, Debug, Deserialize)] +pub struct LoggingParams { + pub interaction_ids: Option>, + pub device_identifier: Option, + pub command_initiated_time: Option, + pub page_instance_ids: Option>, + pub command_id: Option, +} diff --git a/native/vendor/librespot-core/src/deserialize_with.rs b/native/vendor/librespot-core/src/deserialize_with.rs new file mode 100644 index 00000000..22fc24a6 --- /dev/null +++ b/native/vendor/librespot-core/src/deserialize_with.rs @@ -0,0 +1,90 @@ +use base64::Engine; +use base64::prelude::BASE64_STANDARD; +use protobuf::MessageFull; +use serde::de::{Error, Unexpected}; +use serde::{Deserialize, Deserializer}; +use serde_json::Value; + +const IGNORE_UNKNOWN: protobuf_json_mapping::ParseOptions = protobuf_json_mapping::ParseOptions { + ignore_unknown_fields: true, + _future_options: (), +}; + +fn parse_value_to_msg( + value: &Value, +) -> Result { + protobuf_json_mapping::parse_from_str_with_options::(&value.to_string(), &IGNORE_UNKNOWN) +} + +pub fn base64_proto<'de, T, D>(de: D) -> Result, D::Error> +where + T: MessageFull, + D: Deserializer<'de>, +{ + let v: String = Deserialize::deserialize(de)?; + let bytes = BASE64_STANDARD + .decode(v) + .map_err(|e| Error::custom(e.to_string()))?; + + T::parse_from_bytes(&bytes).map(Some).map_err(Error::custom) +} + +pub fn json_proto<'de, T, D>(de: D) -> Result +where + T: MessageFull, + D: Deserializer<'de>, +{ + let v: Value = Deserialize::deserialize(de)?; + parse_value_to_msg(&v).map_err(Error::custom) +} + +pub fn option_json_proto<'de, T, D>(de: D) -> Result, D::Error> +where + T: MessageFull, + D: Deserializer<'de>, +{ + let v: Value = Deserialize::deserialize(de)?; + parse_value_to_msg(&v).map(Some).map_err(Error::custom) +} + +pub fn vec_json_proto<'de, T, D>(de: D) -> Result, D::Error> +where + T: MessageFull, + D: Deserializer<'de>, +{ + let v: Value = Deserialize::deserialize(de)?; + let array = match v { + Value::Array(array) => array, + _ => return Err(Error::custom("the value wasn't an array")), + }; + + let res = array + .iter() + .flat_map(parse_value_to_msg) + .collect::>(); + + Ok(res) +} + +pub fn boxed<'de, T, D>(de: D) -> Result, D::Error> +where + T: Deserialize<'de>, + D: Deserializer<'de>, +{ + let v: T = Deserialize::deserialize(de)?; + Ok(Box::new(v)) +} + +pub fn bool_from_string<'de, D>(de: D) -> Result +where + D: Deserializer<'de>, +{ + match String::deserialize(de)?.as_ref() { + "true" => Ok(true), + "false" => Ok(false), + other => Err(Error::invalid_value( + Unexpected::Str(other), + &"true or false", + )), + } +} diff --git a/native/vendor/librespot-core/src/diffie_hellman.rs b/native/vendor/librespot-core/src/diffie_hellman.rs new file mode 100644 index 00000000..283aa026 --- /dev/null +++ b/native/vendor/librespot-core/src/diffie_hellman.rs @@ -0,0 +1,67 @@ +use std::sync::LazyLock; + +use num_bigint::BigUint; +use num_integer::Integer; +use num_traits::{One, Zero}; +use rand::{CryptoRng, Rng}; + +static DH_GENERATOR: LazyLock = LazyLock::new(|| BigUint::from_bytes_be(&[0x02])); +static DH_PRIME: LazyLock = LazyLock::new(|| { + BigUint::from_bytes_be(&[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, + 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, + 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, + 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, + 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, + 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ]) +}); + +fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { + let mut base = base.clone(); + let mut exp = exp.clone(); + let mut result: BigUint = One::one(); + + while !exp.is_zero() { + if exp.is_odd() { + result = (result * &base) % modulus; + } + exp >>= 1; + base = (&base * &base) % modulus; + } + + result +} + +pub struct DhLocalKeys { + private_key: BigUint, + public_key: BigUint, +} + +impl DhLocalKeys { + pub fn random(rng: &mut R) -> DhLocalKeys { + let mut bytes = [0u8; 95]; + rng.fill_bytes(&mut bytes); + let private_key = BigUint::from_bytes_le(&bytes); + let public_key = powm(&DH_GENERATOR, &private_key, &DH_PRIME); + + DhLocalKeys { + private_key, + public_key, + } + } + + pub fn public_key(&self) -> Vec { + self.public_key.to_bytes_be() + } + + pub fn shared_secret(&self, remote_key: &[u8]) -> Vec { + let shared_key = powm( + &BigUint::from_bytes_be(remote_key), + &self.private_key, + &DH_PRIME, + ); + shared_key.to_bytes_be() + } +} diff --git a/native/vendor/librespot-core/src/error.rs b/native/vendor/librespot-core/src/error.rs new file mode 100644 index 00000000..7f299579 --- /dev/null +++ b/native/vendor/librespot-core/src/error.rs @@ -0,0 +1,507 @@ +use std::{ + error, fmt, + num::{ParseIntError, TryFromIntError}, + str::Utf8Error, + string::FromUtf8Error, +}; + +use base64::DecodeError; +use http::{ + header::{InvalidHeaderName, InvalidHeaderValue, ToStrError}, + method::InvalidMethod, + status::InvalidStatusCode, + uri::{InvalidUri, InvalidUriParts}, +}; +use protobuf::Error as ProtobufError; +use thiserror::Error; +use tokio::sync::{ + AcquireError, TryAcquireError, mpsc::error::SendError, oneshot::error::RecvError, +}; +use url::ParseError; + +use librespot_oauth::OAuthError; + +#[derive(Debug)] +pub struct Error { + pub kind: ErrorKind, + pub error: Box, +} + +#[derive(Clone, Copy, Debug, Eq, Error, Hash, Ord, PartialEq, PartialOrd)] +pub enum ErrorKind { + #[error("The operation was cancelled by the caller")] + Cancelled = 1, + + #[error("Unknown error")] + Unknown = 2, + + #[error("Client specified an invalid argument")] + InvalidArgument = 3, + + #[error("Deadline expired before operation could complete")] + DeadlineExceeded = 4, + + #[error("Requested entity was not found")] + NotFound = 5, + + #[error("Attempt to create entity that already exists")] + AlreadyExists = 6, + + #[error("Permission denied")] + PermissionDenied = 7, + + #[error("No valid authentication credentials")] + Unauthenticated = 16, + + #[error("Resource has been exhausted")] + ResourceExhausted = 8, + + #[error("Invalid state")] + FailedPrecondition = 9, + + #[error("Operation aborted")] + Aborted = 10, + + #[error("Operation attempted past the valid range")] + OutOfRange = 11, + + #[error("Not implemented")] + Unimplemented = 12, + + #[error("Internal error")] + Internal = 13, + + #[error("Service unavailable")] + Unavailable = 14, + + #[error("Unrecoverable data loss or corruption")] + DataLoss = 15, + + #[error("Operation must not be used")] + DoNotUse = -1, +} + +#[derive(Debug, Error)] +struct ErrorMessage(String); + +impl fmt::Display for ErrorMessage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl Error { + pub fn new(kind: ErrorKind, error: E) -> Error + where + E: Into>, + { + Self { + kind, + error: error.into(), + } + } + + pub fn aborted(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Aborted, + error: error.into(), + } + } + + pub fn already_exists(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::AlreadyExists, + error: error.into(), + } + } + + pub fn cancelled(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Cancelled, + error: error.into(), + } + } + + pub fn data_loss(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::DataLoss, + error: error.into(), + } + } + + pub fn deadline_exceeded(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::DeadlineExceeded, + error: error.into(), + } + } + + pub fn do_not_use(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::DoNotUse, + error: error.into(), + } + } + + pub fn failed_precondition(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::FailedPrecondition, + error: error.into(), + } + } + + pub fn internal(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Internal, + error: error.into(), + } + } + + pub fn invalid_argument(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::InvalidArgument, + error: error.into(), + } + } + + pub fn not_found(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::NotFound, + error: error.into(), + } + } + + pub fn out_of_range(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::OutOfRange, + error: error.into(), + } + } + + pub fn permission_denied(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::PermissionDenied, + error: error.into(), + } + } + + pub fn resource_exhausted(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::ResourceExhausted, + error: error.into(), + } + } + + pub fn unauthenticated(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Unauthenticated, + error: error.into(), + } + } + + pub fn unavailable(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Unavailable, + error: error.into(), + } + } + + pub fn unimplemented(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Unimplemented, + error: error.into(), + } + } + + pub fn unknown(error: E) -> Error + where + E: Into>, + { + Self { + kind: ErrorKind::Unknown, + error: error.into(), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.error.source() + } +} + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{} {{ ", self.kind)?; + self.error.fmt(fmt)?; + write!(fmt, " }}") + } +} + +impl From for Error { + fn from(err: OAuthError) -> Self { + use OAuthError::*; + match err { + AuthCodeBadUri { .. } + | AuthCodeNotFound { .. } + | AuthCodeListenerRead + | AuthCodeListenerParse => Error::unavailable(err), + AuthCodeStdinRead + | AuthCodeListenerBind { .. } + | AuthCodeListenerTerminated + | AuthCodeListenerWrite + | Recv + | ExchangeCode { .. } => Error::internal(err), + _ => Error::failed_precondition(err), + } + } +} + +impl From for Error { + fn from(err: DecodeError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: http::Error) -> Self { + if err.is::() + || err.is::() + || err.is::() + || err.is::() + || err.is::() + { + return Self::new(ErrorKind::InvalidArgument, err); + } + + if err.is::() { + return Self::new(ErrorKind::FailedPrecondition, err); + } + + Self::new(ErrorKind::Unknown, err) + } +} + +impl From for Error { + fn from(err: hyper::Error) -> Self { + if err.is_parse() || err.is_parse_status() || err.is_user() { + return Self::new(ErrorKind::Internal, err); + } + + if err.is_canceled() { + return Self::new(ErrorKind::Cancelled, err); + } + + if err.is_incomplete_message() { + return Self::new(ErrorKind::DataLoss, err); + } + + if err.is_body_write_aborted() || err.is_closed() { + return Self::new(ErrorKind::Aborted, err); + } + + if err.is_timeout() { + return Self::new(ErrorKind::DeadlineExceeded, err); + } + + Self::new(ErrorKind::Unknown, err) + } +} + +impl From for Error { + fn from(err: hyper_util::client::legacy::Error) -> Self { + if err.is_connect() { + return Self::new(ErrorKind::Unavailable, err); + } + + Self::new(ErrorKind::Unknown, err) + } +} + +impl From for Error { + fn from(err: time::error::Parse) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: quick_xml::Error) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: serde_json::Error) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: std::io::Error) -> Self { + use std::io::ErrorKind as IoErrorKind; + match err.kind() { + IoErrorKind::NotFound => Self::new(ErrorKind::NotFound, err), + IoErrorKind::PermissionDenied => Self::new(ErrorKind::PermissionDenied, err), + IoErrorKind::AddrInUse | IoErrorKind::AlreadyExists => { + Self::new(ErrorKind::AlreadyExists, err) + } + IoErrorKind::AddrNotAvailable + | IoErrorKind::ConnectionRefused + | IoErrorKind::NotConnected => Self::new(ErrorKind::Unavailable, err), + IoErrorKind::BrokenPipe + | IoErrorKind::ConnectionReset + | IoErrorKind::ConnectionAborted => Self::new(ErrorKind::Aborted, err), + IoErrorKind::Interrupted | IoErrorKind::WouldBlock => { + Self::new(ErrorKind::Cancelled, err) + } + IoErrorKind::InvalidData | IoErrorKind::UnexpectedEof => { + Self::new(ErrorKind::FailedPrecondition, err) + } + IoErrorKind::TimedOut => Self::new(ErrorKind::DeadlineExceeded, err), + IoErrorKind::InvalidInput => Self::new(ErrorKind::InvalidArgument, err), + IoErrorKind::WriteZero => Self::new(ErrorKind::ResourceExhausted, err), + _ => Self::new(ErrorKind::Unknown, err), + } + } +} + +impl From for Error { + fn from(err: FromUtf8Error) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: InvalidHeaderValue) -> Self { + Self::new(ErrorKind::InvalidArgument, err) + } +} + +impl From for Error { + fn from(err: InvalidUri) -> Self { + Self::new(ErrorKind::InvalidArgument, err) + } +} + +impl From for Error { + fn from(err: ParseError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: ParseIntError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: TryFromIntError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: ProtobufError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: RecvError) -> Self { + Self::new(ErrorKind::Internal, err) + } +} + +impl From> for Error { + fn from(err: SendError) -> Self { + Self { + kind: ErrorKind::Internal, + error: ErrorMessage(err.to_string()).into(), + } + } +} + +impl From for Error { + fn from(err: AcquireError) -> Self { + Self { + kind: ErrorKind::ResourceExhausted, + error: ErrorMessage(err.to_string()).into(), + } + } +} + +impl From for Error { + fn from(err: TryAcquireError) -> Self { + Self { + kind: ErrorKind::ResourceExhausted, + error: ErrorMessage(err.to_string()).into(), + } + } +} + +impl From for Error { + fn from(err: ToStrError) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: Utf8Error) -> Self { + Self::new(ErrorKind::FailedPrecondition, err) + } +} + +impl From for Error { + fn from(err: protobuf_json_mapping::ParseError) -> Self { + Self::failed_precondition(err) + } +} diff --git a/native/vendor/librespot-core/src/file_id.rs b/native/vendor/librespot-core/src/file_id.rs new file mode 100644 index 00000000..e513d97d --- /dev/null +++ b/native/vendor/librespot-core/src/file_id.rs @@ -0,0 +1,62 @@ +use std::fmt; + +use librespot_protocol as protocol; + +use crate::{Error, spotify_id::to_base16}; + +const RAW_LEN: usize = 20; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FileId(pub [u8; RAW_LEN]); + +impl FileId { + pub fn from_raw(src: &[u8]) -> FileId { + let mut dst = [0u8; RAW_LEN]; + let len = src.len(); + // some tracks return 16 instead of 20 bytes: #1188 + if len <= RAW_LEN { + dst[..len].clone_from_slice(src); + } + FileId(dst) + } + + #[allow(clippy::wrong_self_convention)] + pub fn to_base16(&self) -> Result { + to_base16(&self.0, &mut [0u8; 40]) + } +} + +impl fmt::Debug for FileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("FileId").field(&self.to_base16()).finish() + } +} + +impl fmt::Display for FileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_base16().unwrap_or_default()) + } +} + +impl From<&[u8]> for FileId { + fn from(src: &[u8]) -> Self { + Self::from_raw(src) + } +} +impl From<&protocol::metadata::Image> for FileId { + fn from(image: &protocol::metadata::Image) -> Self { + Self::from(image.file_id()) + } +} + +impl From<&protocol::metadata::AudioFile> for FileId { + fn from(file: &protocol::metadata::AudioFile) -> Self { + Self::from(file.file_id()) + } +} + +impl From<&protocol::metadata::VideoFile> for FileId { + fn from(video: &protocol::metadata::VideoFile) -> Self { + Self::from(video.file_id()) + } +} diff --git a/native/vendor/librespot-core/src/http_client.rs b/native/vendor/librespot-core/src/http_client.rs new file mode 100644 index 00000000..bbd63838 --- /dev/null +++ b/native/vendor/librespot-core/src/http_client.rs @@ -0,0 +1,304 @@ +use std::{ + sync::OnceLock, + time::{Duration, Instant}, +}; + +use bytes::Bytes; +use futures_util::{FutureExt, future::IntoStream}; +use governor::{ + Quota, RateLimiter, clock::MonotonicClock, middleware::NoOpMiddleware, + state::keyed::DefaultKeyedStateStore, +}; +use http::{Uri, header::HeaderValue}; +use http_body_util::{BodyExt, Full}; +use hyper::{HeaderMap, Request, Response, StatusCode, body::Incoming, header::USER_AGENT}; +use hyper_proxy2::{Intercept, Proxy, ProxyConnector}; +use hyper_util::{ + client::legacy::{Client, ResponseFuture, connect::HttpConnector}, + rt::TokioExecutor, +}; +use nonzero_ext::nonzero; +use thiserror::Error; +use url::Url; + +#[cfg(all(feature = "__rustls", not(feature = "native-tls")))] +use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +#[cfg(all(feature = "native-tls", not(feature = "__rustls")))] +use hyper_tls::HttpsConnector; + +use crate::{ + Error, + config::{OS, os_version}, + date::Date, + version::{FALLBACK_USER_AGENT, VERSION_STRING, spotify_version}, +}; + +// The 30 seconds interval is documented by Spotify, but the calls per interval +// is a guesstimate and probably subject to licensing (purchasing extra calls) +// and may change at any time. +pub const RATE_LIMIT_INTERVAL: Duration = Duration::from_secs(30); +pub const RATE_LIMIT_MAX_WAIT: Duration = Duration::from_secs(10); +pub const RATE_LIMIT_CALLS_PER_INTERVAL: u32 = 300; + +#[derive(Debug, Error)] +pub enum HttpClientError { + #[error("Response status code: {0}")] + StatusCode(hyper::StatusCode), +} + +impl From for Error { + fn from(err: HttpClientError) -> Self { + match err { + HttpClientError::StatusCode(code) => { + // not exhaustive, but what reasonably could be expected + match code { + StatusCode::GATEWAY_TIMEOUT | StatusCode::REQUEST_TIMEOUT => { + Error::deadline_exceeded(err) + } + StatusCode::GONE + | StatusCode::NOT_FOUND + | StatusCode::MOVED_PERMANENTLY + | StatusCode::PERMANENT_REDIRECT + | StatusCode::TEMPORARY_REDIRECT => Error::not_found(err), + StatusCode::FORBIDDEN | StatusCode::PAYMENT_REQUIRED => { + Error::permission_denied(err) + } + StatusCode::NETWORK_AUTHENTICATION_REQUIRED + | StatusCode::PROXY_AUTHENTICATION_REQUIRED + | StatusCode::UNAUTHORIZED => Error::unauthenticated(err), + StatusCode::EXPECTATION_FAILED + | StatusCode::PRECONDITION_FAILED + | StatusCode::PRECONDITION_REQUIRED => Error::failed_precondition(err), + StatusCode::RANGE_NOT_SATISFIABLE => Error::out_of_range(err), + StatusCode::INTERNAL_SERVER_ERROR + | StatusCode::MISDIRECTED_REQUEST + | StatusCode::SERVICE_UNAVAILABLE + | StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS => Error::unavailable(err), + StatusCode::BAD_REQUEST + | StatusCode::HTTP_VERSION_NOT_SUPPORTED + | StatusCode::LENGTH_REQUIRED + | StatusCode::METHOD_NOT_ALLOWED + | StatusCode::NOT_ACCEPTABLE + | StatusCode::PAYLOAD_TOO_LARGE + | StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE + | StatusCode::UNSUPPORTED_MEDIA_TYPE + | StatusCode::URI_TOO_LONG => Error::invalid_argument(err), + StatusCode::TOO_MANY_REQUESTS => Error::resource_exhausted(err), + StatusCode::NOT_IMPLEMENTED => Error::unimplemented(err), + _ => Error::unknown(err), + } + } + } + } +} + +type HyperClient = Client>, Full>; + +pub struct HttpClient { + user_agent: HeaderValue, + proxy_url: Option, + hyper_client: OnceLock, + + rate_limiter: + RateLimiter, MonotonicClock, NoOpMiddleware>, +} + +impl HttpClient { + pub fn new(proxy_url: Option<&Url>) -> Self { + let zero_str = String::from("0"); + let os_version = os_version(); + + let (spotify_platform, os_version) = match OS { + "android" => ("Android", os_version), + "ios" => ("iOS", os_version), + "macos" => ("OSX", zero_str), + "windows" => ("Win32", zero_str), + _ => ("Linux", zero_str), + }; + + let user_agent_str = &format!( + "Spotify/{} {}/{} ({})", + spotify_version(), + spotify_platform, + os_version, + VERSION_STRING + ); + + let user_agent = HeaderValue::from_str(user_agent_str).unwrap_or_else(|err| { + error!("Invalid user agent <{user_agent_str}>: {err}"); + HeaderValue::from_static(FALLBACK_USER_AGENT) + }); + + let replenish_interval_ns = + RATE_LIMIT_INTERVAL.as_nanos() / RATE_LIMIT_CALLS_PER_INTERVAL as u128; + let quota = Quota::with_period(Duration::from_nanos(replenish_interval_ns as u64)) + .expect("replenish interval should be valid") + .allow_burst(nonzero![RATE_LIMIT_CALLS_PER_INTERVAL]); + let rate_limiter = RateLimiter::keyed(quota); + + Self { + user_agent, + proxy_url: proxy_url.cloned(), + hyper_client: OnceLock::new(), + rate_limiter, + } + } + + fn try_create_hyper_client(proxy_url: Option<&Url>) -> Result { + // configuring TLS is expensive and should be done once per process + + #[cfg(all(feature = "__rustls", not(feature = "native-tls")))] + let https_connector = { + #[cfg(feature = "rustls-tls-native-roots")] + let tls = HttpsConnectorBuilder::new().with_native_roots()?; + #[cfg(feature = "rustls-tls-webpki-roots")] + let tls = HttpsConnectorBuilder::new().with_webpki_roots(); + tls.https_or_http().enable_http1().enable_http2().build() + }; + + #[cfg(all(feature = "native-tls", not(feature = "__rustls")))] + let https_connector = HttpsConnector::new(); + + // When not using a proxy a dummy proxy is configured that will not intercept any traffic. + // This prevents needing to carry the Client Connector generics through the whole project + let proxy = match &proxy_url { + Some(proxy_url) => Proxy::new(Intercept::All, proxy_url.to_string().parse()?), + None => Proxy::new(Intercept::None, Uri::from_static("0.0.0.0")), + }; + let proxy_connector = ProxyConnector::from_proxy(https_connector, proxy)?; + + let client = Client::builder(TokioExecutor::new()) + .http2_adaptive_window(true) + .build(proxy_connector); + Ok(client) + } + + fn hyper_client(&self) -> &HyperClient { + self.hyper_client + .get_or_init(|| Self::try_create_hyper_client(self.proxy_url.as_ref()).unwrap()) + } + + pub async fn request(&self, req: Request) -> Result, Error> { + debug!("Requesting {}", req.uri()); + + // `Request` does not implement `Clone` because its `Body` may be a single-shot stream. + // As correct as that may be technically, we now need all this boilerplate to clone it + // ourselves, as any `Request` is moved in the loop. + let (parts, body_as_bytes) = req.into_parts(); + + loop { + let mut req = Request::builder() + .method(parts.method.clone()) + .uri(parts.uri.clone()) + .version(parts.version) + .body(body_as_bytes.clone())?; + *req.headers_mut() = parts.headers.clone(); + + let request = self.request_fut(req)?; + let response = request.await; + + if let Ok(response) = &response { + let code = response.status(); + + if code == StatusCode::TOO_MANY_REQUESTS { + if let Some(duration) = Self::get_retry_after(response.headers()) { + warn!( + "Rate limited by service, retrying in {} seconds...", + duration.as_secs() + ); + tokio::time::sleep(duration).await; + continue; + } + } + + if !code.is_success() { + return Err(HttpClientError::StatusCode(code).into()); + } + } + + let response = response?; + return Ok(response); + } + } + + pub async fn request_body(&self, req: Request) -> Result { + let response = self.request(req).await?; + Ok(response.into_body().collect().await?.to_bytes()) + } + + pub fn request_stream(&self, req: Request) -> Result, Error> { + Ok(self.request_fut(req)?.into_stream()) + } + + pub fn request_fut(&self, mut req: Request) -> Result { + let headers_mut = req.headers_mut(); + headers_mut.insert(USER_AGENT, self.user_agent.clone()); + + // For rate limiting we cannot *just* depend on Spotify sending us HTTP/429 + // Retry-After headers. For example, when there is a service interruption + // and HTTP/500 is returned, we don't want to DoS the Spotify infrastructure. + let domain = match req.uri().host() { + Some(host) => { + // strip the prefix from *.domain.tld (assume rate limit is per domain, not subdomain) + let mut parts = host + .split('.') + .map(|s| s.to_string()) + .collect::>(); + let n = parts.len().saturating_sub(2); + parts.drain(n..).collect() + } + None => String::from(""), + }; + self.rate_limiter.check_key(&domain).map_err(|e| { + Error::resource_exhausted(format!( + "rate limited for at least another {} seconds", + e.wait_time_from(Instant::now()).as_secs() + )) + })?; + + Ok(self.hyper_client().request(req.map(Full::new))) + } + + pub fn get_retry_after(headers: &HeaderMap) -> Option { + let now = Date::now_utc().as_timestamp_ms(); + + let mut retry_after_ms = None; + if let Some(header_val) = headers.get("X-RateLimit-Next") { + // *.akamaized.net (Akamai) + if let Ok(date_str) = header_val.to_str() { + if let Ok(target) = Date::from_iso8601(date_str) { + retry_after_ms = Some(target.as_timestamp_ms().saturating_sub(now)) + } + } + } else if let Some(header_val) = headers.get("Fastly-RateLimit-Reset") { + // *.scdn.co (Fastly) + if let Ok(timestamp) = header_val.to_str() { + if let Ok(target) = timestamp.parse::() { + retry_after_ms = Some(target.saturating_sub(now)) + } + } + } else if let Some(header_val) = headers.get("Retry-After") { + // Generic RFC compliant (including *.spotify.com) + if let Ok(retry_after) = header_val.to_str() { + if let Ok(duration) = retry_after.parse::() { + retry_after_ms = Some(duration * 1000) + } + } + } + + if let Some(retry_after) = retry_after_ms { + let duration = Duration::from_millis(retry_after as u64); + if duration <= RATE_LIMIT_MAX_WAIT { + return Some(duration); + } else { + debug!( + "Waiting {} seconds would exceed {} second limit", + duration.as_secs(), + RATE_LIMIT_MAX_WAIT.as_secs() + ); + } + } + + None + } +} diff --git a/native/vendor/librespot-core/src/lib.rs b/native/vendor/librespot-core/src/lib.rs new file mode 100644 index 00000000..f4ead234 --- /dev/null +++ b/native/vendor/librespot-core/src/lib.rs @@ -0,0 +1,46 @@ +#[macro_use] +extern crate log; + +use librespot_protocol as protocol; + +#[macro_use] +mod component; + +pub mod apresolve; +pub mod audio_key; +pub mod authentication; +pub mod cache; +pub mod cdn_url; +pub mod channel; +pub mod config; +mod connection; +pub mod date; +#[allow(dead_code)] +pub mod dealer; +pub mod deserialize_with; +#[doc(hidden)] +pub mod diffie_hellman; +pub mod error; +pub mod file_id; +pub mod http_client; +pub mod login5; +pub mod mercury; +pub mod packet; +mod proxytunnel; +pub mod session; +mod socket; +#[allow(dead_code)] +pub mod spclient; +pub mod spotify_id; +pub mod spotify_uri; +pub mod token; +#[doc(hidden)] +pub mod util; +pub mod version; + +pub use config::SessionConfig; +pub use error::Error; +pub use file_id::FileId; +pub use session::Session; +pub use spotify_id::SpotifyId; +pub use spotify_uri::SpotifyUri; diff --git a/native/vendor/librespot-core/src/login5.rs b/native/vendor/librespot-core/src/login5.rs new file mode 100644 index 00000000..9cf71338 --- /dev/null +++ b/native/vendor/librespot-core/src/login5.rs @@ -0,0 +1,270 @@ +use crate::config::OS; +use crate::spclient::CLIENT_TOKEN; +use crate::token::Token; +use crate::{Error, SessionConfig, util}; +use bytes::Bytes; +use http::{HeaderValue, Method, Request, header::ACCEPT}; +use librespot_protocol::login5::login_response::Response; +use librespot_protocol::{ + client_info::ClientInfo, + credentials::{Password, StoredCredential}, + hashcash::HashcashSolution, + login5::{ + ChallengeSolution, LoginError, LoginOk, LoginRequest, LoginResponse, + login_request::Login_method, + }, +}; +use protobuf::well_known_types::duration::Duration as ProtoDuration; +use protobuf::{Message, MessageField}; +use std::time::{Duration, SystemTime}; +use thiserror::Error; +use tokio::time::sleep; + +const MAX_LOGIN_TRIES: u8 = 3; +const LOGIN_TIMEOUT: Duration = Duration::from_secs(3); + +component! { + Login5Manager : Login5ManagerInner { + auth_token: Option = None, + } +} + +#[derive(Debug, Error)] +enum Login5Error { + #[error("Login request was denied: {0:?}")] + FaultyRequest(LoginError), + #[error("Code challenge is not supported")] + CodeChallenge, + #[error("Tried to acquire token without stored credentials")] + NoStoredCredentials, + #[error("Couldn't successfully authenticate after {0} times")] + RetriesFailed(u8), + #[error("Login via login5 is only allowed for android or ios")] + OnlyForMobile, +} + +impl From for Error { + fn from(err: Login5Error) -> Self { + match err { + Login5Error::NoStoredCredentials | Login5Error::OnlyForMobile => { + Error::unavailable(err) + } + Login5Error::RetriesFailed(_) | Login5Error::FaultyRequest(_) => { + Error::failed_precondition(err) + } + Login5Error::CodeChallenge => Error::unimplemented(err), + } + } +} + +impl Login5Manager { + async fn request(&self, message: &LoginRequest) -> Result { + let client_token = self.session().spclient().client_token().await?; + let body = message.write_to_bytes()?; + + let request = Request::builder() + .method(&Method::POST) + .uri("https://login5.spotify.com/v3/login") + .header(ACCEPT, HeaderValue::from_static("application/x-protobuf")) + .header(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?) + .body(body.into())?; + + self.session().http_client().request_body(request).await + } + + async fn login5_request(&self, login: Login_method) -> Result { + let client_id = match OS { + "macos" | "windows" => self.session().client_id(), + // StoredCredential is used to get an access_token from Session credentials. + // Using the session client_id allows user to use Keymaster on Android/IOS + // if their Credentials::with_access_token was obtained there, assuming + // they have overriden the SessionConfig::client_id with the Keymaster's. + _ if matches!(login, Login_method::StoredCredential(_)) => self.session().client_id(), + _ => SessionConfig::default().client_id, + }; + + let mut login_request = LoginRequest { + client_info: MessageField::some(ClientInfo { + client_id, + device_id: self.session().device_id().to_string(), + special_fields: Default::default(), + }), + login_method: Some(login), + ..Default::default() + }; + + let mut response = self.request(&login_request).await?; + let mut count = 0; + + loop { + count += 1; + + let message = LoginResponse::parse_from_bytes(&response)?; + if let Some(Response::Ok(ok)) = message.response { + break Ok(ok); + } + + if message.has_error() { + match message.error() { + LoginError::TIMEOUT | LoginError::TOO_MANY_ATTEMPTS => { + sleep(LOGIN_TIMEOUT).await + } + others => return Err(Login5Error::FaultyRequest(others).into()), + } + } + + if message.has_challenges() { + // handles the challenges, and updates the login context with the response + Self::handle_challenges(&mut login_request, message)?; + } + + if count < MAX_LOGIN_TRIES { + response = self.request(&login_request).await?; + } else { + return Err(Login5Error::RetriesFailed(MAX_LOGIN_TRIES).into()); + } + } + } + + /// Login for android and ios + /// + /// This request doesn't require a connected session as it is the entrypoint for android or ios + /// + /// This request will only work when: + /// - client_id => android or ios | can be easily adjusted in [SessionConfig::default_for_os] + /// - user-agent => android or ios | has to be adjusted in [HttpClient::new](crate::http_client::HttpClient::new) + pub async fn login( + &self, + id: impl Into, + password: impl Into, + ) -> Result<(Token, Vec), Error> { + if !matches!(OS, "android" | "ios") { + // by manipulating the user-agent and client-id it can be also used/tested on desktop + return Err(Login5Error::OnlyForMobile.into()); + } + + let method = Login_method::Password(Password { + id: id.into(), + password: password.into(), + ..Default::default() + }); + + let token_response = self.login5_request(method).await?; + let auth_token = Self::token_from_login( + token_response.access_token, + token_response.access_token_expires_in, + ); + + Ok((auth_token, token_response.stored_credential)) + } + + /// Retrieve the access_token via login5 + /// + /// This request will only work when the store credentials match the client-id. Meaning that + /// stored credentials generated with the keymaster client-id will not work, for example, with + /// the android client-id. + pub async fn auth_token(&self) -> Result { + let auth_data = self.session().auth_data(); + if auth_data.is_empty() { + return Err(Login5Error::NoStoredCredentials.into()); + } + + let auth_token = self.lock(|inner| { + if let Some(token) = &inner.auth_token { + if token.is_expired() { + inner.auth_token = None; + } + } + inner.auth_token.clone() + }); + + if let Some(auth_token) = auth_token { + return Ok(auth_token); + } + + let method = Login_method::StoredCredential(StoredCredential { + username: self.session().username().to_string(), + data: auth_data, + ..Default::default() + }); + + let token_response = self.login5_request(method).await?; + let auth_token = Self::token_from_login( + token_response.access_token, + token_response.access_token_expires_in, + ); + + let token = self.lock(|inner| { + inner.auth_token = Some(auth_token.clone()); + inner.auth_token.clone() + }); + + trace!("Got auth token: {auth_token:?}"); + + token.ok_or(Login5Error::NoStoredCredentials.into()) + } + + fn handle_challenges( + login_request: &mut LoginRequest, + message: LoginResponse, + ) -> Result<(), Error> { + let challenges = message.challenges(); + debug!( + "Received {} challenges, solving...", + challenges.challenges.len() + ); + + for challenge in &challenges.challenges { + if challenge.has_code() { + return Err(Login5Error::CodeChallenge.into()); + } else if !challenge.has_hashcash() { + debug!("Challenge was empty, skipping..."); + continue; + } + + let hash_cash_challenge = challenge.hashcash(); + + let mut suffix = [0u8; 0x10]; + let duration = util::solve_hash_cash( + &message.login_context, + &hash_cash_challenge.prefix, + hash_cash_challenge.length, + &mut suffix, + )?; + + let (seconds, nanos) = (duration.as_secs() as i64, duration.subsec_nanos() as i32); + debug!("Solving hashcash took {seconds}s {nanos}ns"); + + let mut solution = ChallengeSolution::new(); + solution.set_hashcash(HashcashSolution { + suffix: Vec::from(suffix), + duration: MessageField::some(ProtoDuration { + seconds, + nanos, + ..Default::default() + }), + ..Default::default() + }); + + login_request + .challenge_solutions + .mut_or_insert_default() + .solutions + .push(solution); + } + + login_request.login_context = message.login_context; + + Ok(()) + } + + fn token_from_login(token: String, expires_in: i32) -> Token { + Token { + access_token: token, + expires_in: Duration::from_secs(expires_in.try_into().unwrap_or(3600)), + token_type: "Bearer".to_string(), + scopes: vec![], + timestamp: SystemTime::now(), + } + } +} diff --git a/native/vendor/librespot-core/src/mercury/mod.rs b/native/vendor/librespot-core/src/mercury/mod.rs new file mode 100644 index 00000000..b32aeb11 --- /dev/null +++ b/native/vendor/librespot-core/src/mercury/mod.rs @@ -0,0 +1,306 @@ +use std::{ + collections::HashMap, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use byteorder::{BigEndian, ByteOrder}; +use bytes::Bytes; +use futures_util::FutureExt; +use protobuf::Message; +use tokio::sync::{mpsc, oneshot}; + +use crate::{Error, packet::PacketType, protocol, util::SeqGenerator}; + +mod types; +pub use self::types::*; + +mod sender; +pub use self::sender::MercurySender; + +component! { + MercuryManager : MercuryManagerInner { + sequence: SeqGenerator = SeqGenerator::new(0), + pending: HashMap, MercuryPending> = HashMap::new(), + subscriptions: Vec<(String, mpsc::UnboundedSender)> = Vec::new(), + invalid: bool = false, + } +} + +pub struct MercuryPending { + parts: Vec>, + partial: Option>, + callback: Option>>, +} + +pub struct MercuryFuture { + receiver: oneshot::Receiver>, +} + +impl Future for MercuryFuture { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.receiver.poll_unpin(cx)? + } +} + +impl MercuryManager { + fn next_seq(&self) -> Vec { + let mut seq = vec![0u8; 8]; + BigEndian::write_u64(&mut seq, self.lock(|inner| inner.sequence.get())); + seq + } + + fn request(&self, req: MercuryRequest) -> Result, Error> { + let (tx, rx) = oneshot::channel(); + + let pending = MercuryPending { + parts: Vec::new(), + partial: None, + callback: Some(tx), + }; + + let seq = self.next_seq(); + self.lock(|inner| { + if !inner.invalid { + inner.pending.insert(seq.clone(), pending); + } + }); + + let cmd = req.method.command(); + let data = req.encode(&seq)?; + + self.session().send_packet(cmd, data)?; + Ok(MercuryFuture { receiver: rx }) + } + + pub fn get>(&self, uri: T) -> Result, Error> { + self.request(MercuryRequest { + method: MercuryMethod::Get, + uri: uri.into(), + content_type: None, + payload: Vec::new(), + }) + } + + pub fn send>( + &self, + uri: T, + data: Vec, + ) -> Result, Error> { + self.request(MercuryRequest { + method: MercuryMethod::Send, + uri: uri.into(), + content_type: None, + payload: vec![data], + }) + } + + pub fn sender>(&self, uri: T) -> MercurySender { + MercurySender::new(self.clone(), uri.into()) + } + + pub fn subscribe>( + &self, + uri: T, + ) -> impl Future, Error>> + 'static + { + let uri = uri.into(); + let request = self.request(MercuryRequest { + method: MercuryMethod::Sub, + uri: uri.clone(), + content_type: None, + payload: Vec::new(), + }); + + let manager = self.clone(); + async move { + let response = request?.await?; + + let (tx, rx) = mpsc::unbounded_channel(); + + manager.lock(move |inner| { + if !inner.invalid { + debug!("subscribed uri={} count={}", uri, response.payload.len()); + if !response.payload.is_empty() { + // Old subscription protocol, watch the provided list of URIs + for sub in response.payload { + match protocol::pubsub::Subscription::parse_from_bytes(&sub) { + Ok(mut sub) => { + let sub_uri = sub.take_uri(); + + debug!("subscribed sub_uri={sub_uri}"); + + inner.subscriptions.push((sub_uri, tx.clone())); + } + Err(e) => { + error!("could not subscribe to {uri}: {e}"); + } + } + } + } else { + // New subscription protocol, watch the requested URI + inner.subscriptions.push((uri, tx)); + } + } + }); + + Ok(rx) + } + } + + pub fn listen_for>( + &self, + uri: T, + ) -> impl Future> + 'static { + let uri = uri.into(); + + let manager = self.clone(); + async move { + let (tx, rx) = mpsc::unbounded_channel(); + + manager.lock(move |inner| { + if !inner.invalid { + debug!("listening to uri={uri}"); + inner.subscriptions.push((uri, tx)); + } + }); + + rx + } + } + + pub(crate) fn dispatch(&self, cmd: PacketType, mut data: Bytes) -> Result<(), Error> { + let seq_len = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; + let seq = data.split_to(seq_len).as_ref().to_owned(); + + let flags = data.split_to(1).as_ref()[0]; + let count = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; + + let pending = self.lock(|inner| inner.pending.remove(&seq)); + + let mut pending = match pending { + Some(pending) => pending, + None => { + if let PacketType::MercuryEvent = cmd { + MercuryPending { + parts: Vec::new(), + partial: None, + callback: None, + } + } else { + warn!("Ignore seq {:?} cmd {:x}", seq, cmd as u8); + return Err(MercuryError::Command(cmd).into()); + } + } + }; + + for i in 0..count { + let mut part = Self::parse_part(&mut data); + if let Some(mut partial) = pending.partial.take() { + partial.extend_from_slice(&part); + part = partial; + } + + if i == count - 1 && (flags == 2) { + pending.partial = Some(part) + } else { + pending.parts.push(part); + } + } + + if flags == 0x1 { + self.complete_request(cmd, pending)?; + } else { + self.lock(move |inner| inner.pending.insert(seq, pending)); + } + + Ok(()) + } + + fn parse_part(data: &mut Bytes) -> Vec { + let size = BigEndian::read_u16(data.split_to(2).as_ref()) as usize; + data.split_to(size).as_ref().to_owned() + } + + fn complete_request(&self, cmd: PacketType, mut pending: MercuryPending) -> Result<(), Error> { + let header_data = pending.parts.remove(0); + let header = protocol::mercury::Header::parse_from_bytes(&header_data)?; + + let response = MercuryResponse { + uri: header.uri().to_string(), + status_code: header.status_code(), + payload: pending.parts, + }; + + let status_code = response.status_code; + if status_code >= 500 { + error!("error {} for uri {}", status_code, &response.uri); + Err(MercuryError::Response(response).into()) + } else if status_code >= 400 { + error!("error {} for uri {}", status_code, &response.uri); + if let Some(cb) = pending.callback { + cb.send(Err(MercuryError::Response(response.clone()).into())) + .map_err(|_| MercuryError::Channel)?; + } + Err(MercuryError::Response(response).into()) + } else if let PacketType::MercuryEvent = cmd { + // TODO: This is just a workaround to make utf-8 encoded usernames work. + // A better solution would be to use an uri struct and urlencode it directly + // before sending while saving the subscription under its unencoded form. + let mut uri_split = response.uri.split('/'); + + let encoded_uri = std::iter::once(uri_split.next().unwrap_or_default().to_string()) + .chain(uri_split.map(|component| { + form_urlencoded::byte_serialize(component.as_bytes()).collect::() + })) + .collect::>() + .join("/"); + + let mut found = false; + + self.lock(|inner| { + inner.subscriptions.retain(|(prefix, sub)| { + if encoded_uri.starts_with(prefix) { + found = true; + + // if send fails, remove from list of subs + // TODO: send unsub message + sub.send(response.clone()).is_ok() + } else { + // URI doesn't match + true + } + }); + }); + + if found { + Ok(()) + } else if self.session().dealer().handles(&response.uri) { + trace!("mercury response <{}> is handled by dealer", response.uri); + Ok(()) + } else { + debug!("unknown subscription uri={}", &response.uri); + trace!("response pushed over Mercury: {response:?}"); + Err(MercuryError::Response(response).into()) + } + } else if let Some(cb) = pending.callback { + cb.send(Ok(response)).map_err(|_| MercuryError::Channel)?; + Ok(()) + } else { + error!("can't handle Mercury response: {response:?}"); + Err(MercuryError::Response(response).into()) + } + } + + pub(crate) fn shutdown(&self) { + self.lock(|inner| { + inner.invalid = true; + // destroy the sending halves of the channels to signal everyone who is waiting for something. + inner.pending.clear(); + inner.subscriptions.clear(); + }); + } +} diff --git a/native/vendor/librespot-core/src/mercury/sender.rs b/native/vendor/librespot-core/src/mercury/sender.rs new file mode 100644 index 00000000..31409e88 --- /dev/null +++ b/native/vendor/librespot-core/src/mercury/sender.rs @@ -0,0 +1,56 @@ +use std::collections::VecDeque; + +use super::{MercuryFuture, MercuryManager, MercuryResponse}; + +use crate::Error; + +pub struct MercurySender { + mercury: MercuryManager, + uri: String, + pending: VecDeque>, + buffered_future: Option>, +} + +impl MercurySender { + pub(crate) fn new(mercury: MercuryManager, uri: String) -> MercurySender { + MercurySender { + mercury, + uri, + pending: VecDeque::new(), + buffered_future: None, + } + } + + pub fn is_flushed(&self) -> bool { + self.buffered_future.is_none() && self.pending.is_empty() + } + + pub fn send(&mut self, item: Vec) -> Result<(), Error> { + let task = self.mercury.send(self.uri.clone(), item)?; + self.pending.push_back(task); + Ok(()) + } + + pub async fn flush(&mut self) -> Result<(), Error> { + if self.buffered_future.is_none() { + self.buffered_future = self.pending.pop_front(); + } + + while let Some(fut) = self.buffered_future.as_mut() { + fut.await?; + self.buffered_future = self.pending.pop_front(); + } + Ok(()) + } +} + +impl Clone for MercurySender { + fn clone(&self) -> MercurySender { + MercurySender { + mercury: self.mercury.clone(), + uri: self.uri.clone(), + pending: VecDeque::new(), + buffered_future: None, + } + } +} diff --git a/native/vendor/librespot-core/src/mercury/types.rs b/native/vendor/librespot-core/src/mercury/types.rs new file mode 100644 index 00000000..3cc8d8bc --- /dev/null +++ b/native/vendor/librespot-core/src/mercury/types.rs @@ -0,0 +1,101 @@ +use std::io::Write; + +use byteorder::{BigEndian, WriteBytesExt}; +use protobuf::Message; +use thiserror::Error; + +use crate::{Error, packet::PacketType, protocol}; + +#[derive(Debug, PartialEq, Eq)] +pub enum MercuryMethod { + Get, + Sub, + Unsub, + Send, +} + +#[derive(Debug)] +pub struct MercuryRequest { + pub method: MercuryMethod, + pub uri: String, + pub content_type: Option, + pub payload: Vec>, +} + +#[derive(Debug, Clone)] +pub struct MercuryResponse { + pub uri: String, + pub status_code: i32, + pub payload: Vec>, +} + +#[derive(Debug, Error)] +pub enum MercuryError { + #[error("callback receiver was disconnected")] + Channel, + #[error("error handling packet type: {0:?}")] + Command(PacketType), + #[error("error handling Mercury response: {0:?}")] + Response(MercuryResponse), +} + +impl From for Error { + fn from(err: MercuryError) -> Self { + match err { + MercuryError::Channel => Error::aborted(err), + MercuryError::Command(_) => Error::unimplemented(err), + MercuryError::Response(_) => Error::unavailable(err), + } + } +} + +impl std::fmt::Display for MercuryMethod { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match *self { + MercuryMethod::Get => "GET", + MercuryMethod::Sub => "SUB", + MercuryMethod::Unsub => "UNSUB", + MercuryMethod::Send => "SEND", + }; + write!(f, "{s}") + } +} + +impl MercuryMethod { + pub fn command(&self) -> PacketType { + use PacketType::*; + match *self { + MercuryMethod::Get | MercuryMethod::Send => MercuryReq, + MercuryMethod::Sub => MercurySub, + MercuryMethod::Unsub => MercuryUnsub, + } + } +} + +impl MercuryRequest { + pub fn encode(&self, seq: &[u8]) -> Result, Error> { + let mut packet = Vec::new(); + packet.write_u16::(seq.len() as u16)?; + packet.write_all(seq)?; + packet.write_u8(1)?; // Flags: FINAL + packet.write_u16::(1 + self.payload.len() as u16)?; // Part count + + let mut header = protocol::mercury::Header::new(); + header.set_uri(self.uri.clone()); + header.set_method(self.method.to_string()); + + if let Some(ref content_type) = self.content_type { + header.set_content_type(content_type.clone()); + } + + packet.write_u16::(header.compute_size() as u16)?; + header.write_to_writer(&mut packet)?; + + for p in &self.payload { + packet.write_u16::(p.len() as u16)?; + packet.write_all(p)?; + } + + Ok(packet) + } +} diff --git a/native/vendor/librespot-core/src/packet.rs b/native/vendor/librespot-core/src/packet.rs new file mode 100644 index 00000000..2f50d158 --- /dev/null +++ b/native/vendor/librespot-core/src/packet.rs @@ -0,0 +1,41 @@ +// Ported from librespot-java. Relicensed under MIT with permission. + +use num_derive::{FromPrimitive, ToPrimitive}; + +#[derive(Debug, Copy, Clone, FromPrimitive, ToPrimitive)] +pub enum PacketType { + SecretBlock = 0x02, + Ping = 0x04, + StreamChunk = 0x08, + StreamChunkRes = 0x09, + ChannelError = 0x0a, + ChannelAbort = 0x0b, + RequestKey = 0x0c, + AesKey = 0x0d, + AesKeyError = 0x0e, + Image = 0x19, + CountryCode = 0x1b, + Pong = 0x49, + PongAck = 0x4a, + Pause = 0x4b, + ProductInfo = 0x50, + LegacyWelcome = 0x69, + LicenseVersion = 0x76, + Login = 0xab, + APWelcome = 0xac, + AuthFailure = 0xad, + MercuryReq = 0xb2, + MercurySub = 0xb3, + MercuryUnsub = 0xb4, + MercuryEvent = 0xb5, + TrackEndedTime = 0x82, + UnknownDataAllZeros = 0x1f, + PreferredLocale = 0x74, + Unknown0x0f = 0x0f, + Unknown0x10 = 0x10, + Unknown0x4f = 0x4f, + + // TODO - occurs when subscribing with an empty URI. Maybe a MercuryError? + // Payload: b"\0\x08\0\0\0\0\0\0\0\0\x01\0\x01\0\x03 \xb0\x06" + Unknown0xb6 = 0xb6, +} diff --git a/native/vendor/librespot-core/src/proxytunnel.rs b/native/vendor/librespot-core/src/proxytunnel.rs new file mode 100644 index 00000000..9e8cd3b7 --- /dev/null +++ b/native/vendor/librespot-core/src/proxytunnel.rs @@ -0,0 +1,52 @@ +use std::io; + +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; + +pub async fn proxy_connect( + mut proxy_connection: T, + connect_host: &str, + connect_port: &str, +) -> io::Result { + let mut buffer = Vec::new(); + buffer.extend_from_slice(b"CONNECT "); + buffer.extend_from_slice(connect_host.as_bytes()); + buffer.push(b':'); + buffer.extend_from_slice(connect_port.as_bytes()); + buffer.extend_from_slice(b" HTTP/1.1\r\n\r\n"); + + proxy_connection.write_all(buffer.as_ref()).await?; + + buffer.resize(buffer.capacity(), 0); + + let mut offset = 0; + loop { + let bytes_read = proxy_connection.read(&mut buffer[offset..]).await?; + if bytes_read == 0 { + return Err(io::Error::other("Early EOF from proxy")); + } + offset += bytes_read; + + let mut headers = [httparse::EMPTY_HEADER; 16]; + let mut response = httparse::Response::new(&mut headers); + + let status = response + .parse(&buffer[..offset]) + .map_err(io::Error::other)?; + + if status.is_complete() { + return match response.code { + Some(200) => Ok(proxy_connection), // Proxy says all is well + Some(code) => { + let reason = response.reason.unwrap_or("no reason"); + let msg = format!("Proxy responded with {code}: {reason}"); + Err(io::Error::other(msg)) + } + None => Err(io::Error::other("Malformed response from proxy")), + }; + } + + if offset >= buffer.len() { + buffer.resize(buffer.len() + 100, 0); + } + } +} diff --git a/native/vendor/librespot-core/src/session.rs b/native/vendor/librespot-core/src/session.rs new file mode 100644 index 00000000..333678fd --- /dev/null +++ b/native/vendor/librespot-core/src/session.rs @@ -0,0 +1,957 @@ +use std::{ + collections::HashMap, + future::Future, + io, + pin::Pin, + process::exit, + sync::{Arc, OnceLock, RwLock, Weak}, + task::{Context, Poll}, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use crate::dealer::manager::DealerManager; +use crate::{ + Error, + apresolve::{ApResolver, SocketAddress}, + audio_key::AudioKeyManager, + authentication::Credentials, + cache::Cache, + channel::ChannelManager, + config::SessionConfig, + connection::{self, AuthenticationError, Transport}, + http_client::HttpClient, + login5::Login5Manager, + mercury::MercuryManager, + packet::PacketType, + protocol::keyexchange::ErrorCode, + spclient::SpClient, + token::TokenProvider, +}; +use byteorder::{BigEndian, ByteOrder}; +use bytes::Bytes; +use futures_core::TryStream; +use futures_util::StreamExt; +use librespot_protocol::authentication::AuthenticationType; +use num_traits::FromPrimitive; +use pin_project_lite::pin_project; +use quick_xml::events::Event; +use thiserror::Error; +use tokio::{ + sync::mpsc, + time::{Duration as TokioDuration, Instant as TokioInstant, Sleep, sleep}, +}; +use tokio_stream::wrappers::UnboundedReceiverStream; +use uuid::Uuid; + +const SESSION_DATA_POISON_MSG: &str = "session data rwlock should not be poisoned"; + +#[derive(Debug, Error)] +pub enum SessionError { + #[error(transparent)] + AuthenticationError(#[from] AuthenticationError), + #[error("Cannot create session: {0}")] + IoError(#[from] io::Error), + #[error("Session is not connected")] + NotConnected, + #[error("packet {0} unknown")] + Packet(u8), +} + +impl From for Error { + fn from(err: SessionError) -> Self { + match err { + SessionError::AuthenticationError(_) => Error::unauthenticated(err), + SessionError::IoError(_) => Error::unavailable(err), + SessionError::NotConnected => Error::unavailable(err), + SessionError::Packet(_) => Error::unimplemented(err), + } + } +} + +impl From for Error { + fn from(err: quick_xml::encoding::EncodingError) -> Self { + Error::invalid_argument(err) + } +} + +pub type UserAttributes = HashMap; + +#[derive(Debug, Clone, Default)] +pub struct UserData { + pub country: String, + pub canonical_username: String, + pub attributes: UserAttributes, +} + +#[derive(Debug, Clone, Default)] +struct SessionData { + session_id: String, + client_id: String, + client_name: String, + client_brand_name: String, + client_model_name: String, + connection_id: String, + auth_data: Vec, + time_delta: i64, + invalid: bool, + user_data: UserData, +} + +struct SessionInternal { + config: SessionConfig, + data: RwLock, + + http_client: HttpClient, + tx_connection: OnceLock)>>, + + apresolver: OnceLock, + audio_key: OnceLock, + channel: OnceLock, + mercury: OnceLock, + dealer: OnceLock, + spclient: OnceLock, + token_provider: OnceLock, + login5: OnceLock, + cache: Option>, + + handle: tokio::runtime::Handle, +} + +/// A shared reference to a Spotify session. +/// +/// After instantiating, you need to login via [Session::connect]. +/// You can either implement the whole playback logic yourself by using +/// this structs interface directly or hand it to a +/// `Player`. +/// +/// *Note*: [Session] instances cannot yet be reused once invalidated. After +/// an unexpectedly closed connection, you'll need to create a new [Session]. +#[derive(Clone)] +pub struct Session(Arc); + +impl Session { + pub fn new(config: SessionConfig, cache: Option) -> Self { + let http_client = HttpClient::new(config.proxy.as_ref()); + + debug!("new Session"); + + let session_data = SessionData { + client_id: config.client_id.clone(), + // can be any guid, doesn't need to be simple + session_id: Uuid::new_v4().as_simple().to_string(), + ..SessionData::default() + }; + + Self(Arc::new(SessionInternal { + config, + data: RwLock::new(session_data), + http_client, + tx_connection: OnceLock::new(), + cache: cache.map(Arc::new), + apresolver: OnceLock::new(), + audio_key: OnceLock::new(), + channel: OnceLock::new(), + mercury: OnceLock::new(), + dealer: OnceLock::new(), + spclient: OnceLock::new(), + token_provider: OnceLock::new(), + login5: OnceLock::new(), + handle: tokio::runtime::Handle::current(), + })) + } + + async fn connect_inner( + &self, + access_point: &SocketAddress, + credentials: Credentials, + ) -> Result<(Credentials, Transport), Error> { + const MAX_RETRIES: u8 = 1; + let mut transport = connection::connect_with_retry( + &access_point.0, + access_point.1, + self.config().proxy.as_ref(), + MAX_RETRIES, + ) + .await?; + let mut reusable_credentials = connection::authenticate( + &mut transport, + credentials.clone(), + &self.config().device_id, + ) + .await?; + + // Might be able to remove this once keymaster is replaced with login5. + if credentials.auth_type == AuthenticationType::AUTHENTICATION_SPOTIFY_TOKEN { + trace!( + "Reconnect using stored credentials as token authed sessions cannot use keymaster." + ); + transport = connection::connect_with_retry( + &access_point.0, + access_point.1, + self.config().proxy.as_ref(), + MAX_RETRIES, + ) + .await?; + reusable_credentials = connection::authenticate( + &mut transport, + reusable_credentials.clone(), + &self.config().device_id, + ) + .await?; + } + + Ok((reusable_credentials, transport)) + } + + pub async fn connect( + &self, + credentials: Credentials, + store_credentials: bool, + ) -> Result<(), Error> { + // There currently happen to be 6 APs but anything will do to avoid an infinite loop. + const MAX_AP_TRIES: u8 = 6; + let mut num_ap_tries = 0; + let (reusable_credentials, transport) = loop { + let ap = self.apresolver().resolve("accesspoint").await?; + info!("Connecting to AP \"{}:{}\"", ap.0, ap.1); + match self.connect_inner(&ap, credentials.clone()).await { + Ok(ct) => break ct, + Err(e) => { + num_ap_tries += 1; + if MAX_AP_TRIES == num_ap_tries { + error!("Tried too many access points"); + return Err(e); + } + if let Some(AuthenticationError::LoginFailed(ErrorCode::TryAnotherAP)) = + e.error.downcast_ref::() + { + warn!("Instructed to try another access point..."); + continue; + } else if let Some(AuthenticationError::LoginFailed(..)) = + e.error.downcast_ref::() + { + return Err(e); + } else { + warn!("Try another access point..."); + continue; + } + } + } + }; + + let username = reusable_credentials + .username + .as_ref() + .map_or("UNKNOWN", |s| s.as_str()); + info!("Authenticated as '{username}' !"); + self.set_username(username); + self.set_auth_data(&reusable_credentials.auth_data); + if let Some(cache) = self.cache() { + if store_credentials { + let cred_changed = cache + .credentials() + .map(|c| c != reusable_credentials) + .unwrap_or(true); + if cred_changed { + cache.save_credentials(&reusable_credentials); + } + } + } + + // This channel serves as a buffer for packets and serializes access to the TcpStream, such + // that `self.send_packet` can return immediately and needs no additional synchronization. + let (tx_connection, rx_connection) = mpsc::unbounded_channel(); + self.0 + .tx_connection + .set(tx_connection) + .map_err(|_| SessionError::NotConnected)?; + + let (sink, stream) = transport.split(); + let sender_task = UnboundedReceiverStream::new(rx_connection) + .map(Ok) + .forward(sink); + let session_weak = self.weak(); + tokio::spawn(async move { + if let Err(e) = sender_task.await { + error!("{e}"); + if let Some(session) = session_weak.try_upgrade() { + if !session.is_invalid() { + session.shutdown(); + } + } + } + }); + + tokio::spawn(DispatchTask::new(self.weak(), stream)); + + Ok(()) + } + + pub fn apresolver(&self) -> &ApResolver { + self.0 + .apresolver + .get_or_init(|| ApResolver::new(self.weak())) + } + + pub fn audio_key(&self) -> &AudioKeyManager { + self.0 + .audio_key + .get_or_init(|| AudioKeyManager::new(self.weak())) + } + + pub fn channel(&self) -> &ChannelManager { + self.0 + .channel + .get_or_init(|| ChannelManager::new(self.weak())) + } + + pub fn http_client(&self) -> &HttpClient { + &self.0.http_client + } + + pub fn mercury(&self) -> &MercuryManager { + self.0 + .mercury + .get_or_init(|| MercuryManager::new(self.weak())) + } + + pub fn dealer(&self) -> &DealerManager { + self.0 + .dealer + .get_or_init(|| DealerManager::new(self.weak())) + } + + pub fn spclient(&self) -> &SpClient { + self.0.spclient.get_or_init(|| SpClient::new(self.weak())) + } + + pub fn token_provider(&self) -> &TokenProvider { + self.0 + .token_provider + .get_or_init(|| TokenProvider::new(self.weak())) + } + + pub fn login5(&self) -> &Login5Manager { + self.0 + .login5 + .get_or_init(|| Login5Manager::new(self.weak())) + } + + pub fn time_delta(&self) -> i64 { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .time_delta + } + + pub fn spawn(&self, task: T) + where + T: Future + Send + 'static, + T::Output: Send + 'static, + { + self.0.handle.spawn(task); + } + + fn debug_info(&self) { + debug!( + "Session strong={} weak={}", + Arc::strong_count(&self.0), + Arc::weak_count(&self.0) + ); + } + + fn check_catalogue(attributes: &UserAttributes) { + if let Some(account_type) = attributes.get("type") { + if account_type != "premium" { + error!("librespot does not support {account_type:?} accounts."); + info!("Please support Spotify and your artists and sign up for a premium account."); + + // TODO: logout instead of exiting + exit(1); + } + } + } + + pub fn send_packet(&self, cmd: PacketType, data: Vec) -> Result<(), Error> { + match self.0.tx_connection.get() { + Some(tx) => Ok(tx.send((cmd as u8, data))?), + None => Err(SessionError::NotConnected.into()), + } + } + + pub fn cache(&self) -> Option<&Arc> { + self.0.cache.as_ref() + } + + pub fn config(&self) -> &SessionConfig { + &self.0.config + } + + // This clones a fairly large struct, so use a specific getter or setter unless + // you need more fields at once, in which case this can spare multiple `read` + // locks. + pub fn user_data(&self) -> UserData { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .clone() + } + + pub fn session_id(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .session_id + .clone() + } + + pub fn set_session_id(&self, session_id: &str) { + session_id.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .session_id, + ); + } + + pub fn device_id(&self) -> &str { + &self.config().device_id + } + + pub fn client_id(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .client_id + .clone() + } + + pub fn set_client_id(&self, client_id: &str) { + client_id.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .client_id, + ); + } + + pub fn client_name(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .client_name + .clone() + } + + pub fn set_client_name(&self, client_name: &str) { + client_name.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .client_name, + ); + } + + pub fn client_brand_name(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .client_brand_name + .clone() + } + + pub fn set_client_brand_name(&self, client_brand_name: &str) { + client_brand_name.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .client_brand_name, + ); + } + + pub fn client_model_name(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .client_model_name + .clone() + } + + pub fn set_client_model_name(&self, client_model_name: &str) { + client_model_name.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .client_model_name, + ); + } + + pub fn connection_id(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .connection_id + .clone() + } + + pub fn set_connection_id(&self, connection_id: &str) { + connection_id.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .connection_id, + ); + } + + pub fn username(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .canonical_username + .clone() + } + + pub fn set_username(&self, username: &str) { + username.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .canonical_username, + ); + } + + pub fn auth_data(&self) -> Vec { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .auth_data + .clone() + } + + pub fn set_auth_data(&self, auth_data: &[u8]) { + auth_data.clone_into( + &mut self + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .auth_data, + ); + } + + pub fn country(&self) -> String { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .country + .clone() + } + + pub fn filter_explicit_content(&self) -> bool { + match self.get_user_attribute("filter-explicit-content") { + Some(value) => matches!(&*value, "1"), + None => false, + } + } + + pub fn autoplay(&self) -> bool { + if let Some(overide) = self.config().autoplay { + return overide; + } + + match self.get_user_attribute("autoplay") { + Some(value) => matches!(&*value, "1"), + None => false, + } + } + + pub fn set_user_attribute(&self, key: &str, value: &str) -> Option { + let mut dummy_attributes = UserAttributes::new(); + dummy_attributes.insert(key.to_owned(), value.to_owned()); + Self::check_catalogue(&dummy_attributes); + + self.0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .attributes + .insert(key.to_owned(), value.to_owned()) + } + + pub fn set_user_attributes(&self, attributes: UserAttributes) { + Self::check_catalogue(&attributes); + + self.0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .attributes + .extend(attributes) + } + + pub fn get_user_attribute(&self, key: &str) -> Option { + self.0 + .data + .read() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .attributes + .get(key) + .cloned() + } + + fn weak(&self) -> SessionWeak { + SessionWeak(Arc::downgrade(&self.0)) + } + + pub fn shutdown(&self) { + debug!("Shutdown: Invalidating session"); + self.0.data.write().expect(SESSION_DATA_POISON_MSG).invalid = true; + self.mercury().shutdown(); + self.channel().shutdown(); + } + + pub fn is_invalid(&self) -> bool { + self.0.data.read().expect(SESSION_DATA_POISON_MSG).invalid + } +} + +#[derive(Clone)] +pub struct SessionWeak(Weak); + +impl SessionWeak { + fn try_upgrade(&self) -> Option { + self.0.upgrade().map(Session) + } + + pub(crate) fn upgrade(&self) -> Session { + self.try_upgrade() + .expect("session was dropped and so should have this component") + } +} + +impl Drop for SessionInternal { + fn drop(&mut self) { + debug!("drop Session"); + } +} + +#[derive(Clone, Copy, Default, Debug, PartialEq)] +enum KeepAliveState { + #[default] + // Expecting a Ping from the server, either after startup or after a PongAck. + ExpectingPing, + + // We need to send a Pong at the given time. + PendingPong, + + // We just sent a Pong and wait for it be ACK'd. + ExpectingPongAck, +} + +const INITIAL_PING_TIMEOUT: TokioDuration = TokioDuration::from_secs(20); +const PING_TIMEOUT: TokioDuration = TokioDuration::from_secs(80); // 60s expected + 20s buffer +const PONG_DELAY: TokioDuration = TokioDuration::from_secs(60); +const PONG_ACK_TIMEOUT: TokioDuration = TokioDuration::from_secs(20); + +impl KeepAliveState { + fn debug(&self, sleep: &Sleep) { + let delay = sleep + .deadline() + .checked_duration_since(TokioInstant::now()) + .map(|t| t.as_secs_f64()) + .unwrap_or(f64::INFINITY); + + trace!("keep-alive state: {self:?}, timeout in {delay:.1}"); + } +} + +pin_project! { + struct DispatchTask + where + S: TryStream + { + session: SessionWeak, + keep_alive_state: KeepAliveState, + #[pin] + stream: S, + #[pin] + timeout: Sleep, + } + + impl PinnedDrop for DispatchTask + where + S: TryStream + { + fn drop(_this: Pin<&mut Self>) { + debug!("drop Dispatch"); + } + } +} + +impl DispatchTask +where + S: TryStream, +{ + fn new(session: SessionWeak, stream: S) -> Self { + Self { + session, + keep_alive_state: KeepAliveState::ExpectingPing, + stream, + timeout: sleep(INITIAL_PING_TIMEOUT), + } + } + + fn dispatch( + mut self: Pin<&mut Self>, + session: &Session, + cmd: u8, + data: Bytes, + ) -> Result<(), Error> { + use KeepAliveState::*; + use PacketType::*; + + let packet_type = FromPrimitive::from_u8(cmd); + let cmd = match packet_type { + Some(cmd) => cmd, + None => { + trace!("Ignoring unknown packet {cmd:x}"); + return Err(SessionError::Packet(cmd).into()); + } + }; + + match packet_type { + Some(Ping) => { + trace!("Received Ping"); + if self.keep_alive_state != ExpectingPing { + warn!("Received unexpected Ping from server") + } + let mut this = self.as_mut().project(); + *this.keep_alive_state = PendingPong; + this.timeout + .as_mut() + .reset(TokioInstant::now() + PONG_DELAY); + this.keep_alive_state.debug(&this.timeout); + + let server_timestamp = BigEndian::read_u32(data.as_ref()) as i64; + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or(Duration::ZERO) + .as_secs() as i64; + { + let mut data = session.0.data.write().expect(SESSION_DATA_POISON_MSG); + data.time_delta = server_timestamp.saturating_sub(timestamp); + } + + session.debug_info(); + + Ok(()) + } + Some(PongAck) => { + trace!("Received PongAck"); + if self.keep_alive_state != ExpectingPongAck { + warn!("Received unexpected PongAck from server") + } + let mut this = self.as_mut().project(); + *this.keep_alive_state = ExpectingPing; + this.timeout + .as_mut() + .reset(TokioInstant::now() + PING_TIMEOUT); + this.keep_alive_state.debug(&this.timeout); + + Ok(()) + } + Some(CountryCode) => { + let country = String::from_utf8(data.as_ref().to_owned())?; + info!("Country: {country:?}"); + session + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .country = country; + Ok(()) + } + Some(StreamChunkRes) | Some(ChannelError) => session.channel().dispatch(cmd, data), + Some(AesKey) | Some(AesKeyError) => session.audio_key().dispatch(cmd, data), + Some(MercuryReq) | Some(MercurySub) | Some(MercuryUnsub) | Some(MercuryEvent) => { + session.mercury().dispatch(cmd, data) + } + Some(ProductInfo) => { + let data = std::str::from_utf8(&data)?; + let mut reader = quick_xml::Reader::from_str(data); + + let mut buf = Vec::new(); + let mut current_element = String::new(); + let mut user_attributes: UserAttributes = HashMap::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(ref element)) => { + std::str::from_utf8(element)?.clone_into(&mut current_element) + } + Ok(Event::End(_)) => { + current_element = String::new(); + } + Ok(Event::Text(ref value)) => { + if !current_element.is_empty() { + let _ = user_attributes.insert( + current_element.clone(), + value.xml_content()?.to_string(), + ); + } + } + Ok(Event::Eof) => break, + Ok(_) => (), + Err(e) => warn!( + "Error parsing XML at position {}: {:?}", + reader.buffer_position(), + e + ), + } + } + + trace!("Received product info: {user_attributes:#?}"); + Session::check_catalogue(&user_attributes); + + session + .0 + .data + .write() + .expect(SESSION_DATA_POISON_MSG) + .user_data + .attributes = user_attributes; + Ok(()) + } + Some(SecretBlock) + | Some(LegacyWelcome) + | Some(UnknownDataAllZeros) + | Some(LicenseVersion) => Ok(()), + _ => { + trace!("Ignoring {cmd:?} packet with data {data:#?}"); + Err(SessionError::Packet(cmd as u8).into()) + } + } + } +} + +impl Future for DispatchTask +where + S: TryStream, + ::Ok: std::fmt::Debug, +{ + type Output = Result<(), S::Error>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use KeepAliveState::*; + + let session = match self.session.try_upgrade() { + Some(session) => session, + None => return Poll::Ready(Ok(())), + }; + + // Process all messages that are immediately ready + loop { + match self.as_mut().project().stream.try_poll_next(cx) { + Poll::Ready(Some(Ok((cmd, data)))) => { + let result = self.as_mut().dispatch(&session, cmd, data); + if let Err(e) = result { + debug!("could not dispatch command: {e}"); + } + } + Poll::Ready(None) => { + warn!("Connection to server closed."); + session.shutdown(); + return Poll::Ready(Ok(())); + } + Poll::Ready(Some(Err(e))) => { + error!("Connection to server closed."); + session.shutdown(); + return Poll::Ready(Err(e)); + } + Poll::Pending => break, + } + } + + // Handle the keep-alive sequence, returning an error when we haven't received a + // Ping/PongAck for too long. + // + // The expected keepalive sequence is + // - Server: Ping + // - wait 60s + // - Client: Pong + // - Server: PongAck + // - wait 60s + // - repeat + // + // This means that we silently lost connection to Spotify servers if + // - we don't receive Ping immediately after connecting, + // - we don't receive a Ping 60s after the last PongAck or + // - we don't receive a PongAck immediately after our Pong. + // + // Currently, we add a safety margin of 20s to these expected deadlines. + let mut this = self.as_mut().project(); + if let Poll::Ready(()) = this.timeout.as_mut().poll(cx) { + match this.keep_alive_state { + ExpectingPing | ExpectingPongAck => { + if !session.is_invalid() { + session.shutdown(); + } + // TODO: Optionally reconnect (with cached/last credentials?) + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::TimedOut, + format!( + "session lost connection to server ({:?})", + this.keep_alive_state + ), + ))); + } + PendingPong => { + trace!("Sending Pong"); + // TODO: Ideally, this should flush the `Framed as Sink` + // before starting the timeout. + let _ = session.send_packet(PacketType::Pong, vec![0, 0, 0, 0]); + *this.keep_alive_state = ExpectingPongAck; + this.timeout + .as_mut() + .reset(TokioInstant::now() + PONG_ACK_TIMEOUT); + this.keep_alive_state.debug(&this.timeout); + } + } + } + + Poll::Pending + } +} diff --git a/native/vendor/librespot-core/src/socket.rs b/native/vendor/librespot-core/src/socket.rs new file mode 100644 index 00000000..71b0d6b9 --- /dev/null +++ b/native/vendor/librespot-core/src/socket.rs @@ -0,0 +1,34 @@ +use std::{io, net::ToSocketAddrs}; + +use tokio::net::TcpStream; +use url::Url; + +use crate::proxytunnel; + +pub async fn connect(host: &str, port: u16, proxy: Option<&Url>) -> io::Result { + let socket = if let Some(proxy_url) = proxy { + info!("Using proxy \"{proxy_url}\""); + + let socket_addr = proxy_url.socket_addrs(|| None).and_then(|addrs| { + addrs.into_iter().next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve proxy server address", + ) + }) + })?; + let socket = TcpStream::connect(&socket_addr).await?; + + proxytunnel::proxy_connect(socket, host, &port.to_string()).await? + } else { + let socket_addr = (host, port).to_socket_addrs()?.next().ok_or_else(|| { + io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve access point address", + ) + })?; + + TcpStream::connect(&socket_addr).await? + }; + Ok(socket) +} diff --git a/native/vendor/librespot-core/src/spclient.rs b/native/vendor/librespot-core/src/spclient.rs new file mode 100644 index 00000000..6eaa33cb --- /dev/null +++ b/native/vendor/librespot-core/src/spclient.rs @@ -0,0 +1,960 @@ +use std::{ + fmt::Write, + time::{Duration, SystemTime}, +}; + +use crate::config::{OS, os_version}; +use crate::{ + Error, FileId, SpotifyId, SpotifyUri, + apresolve::SocketAddress, + config::SessionConfig, + dealer::protocol::TransferOptions, + error::ErrorKind, + protocol::{ + autoplay_context_request::AutoplayContextRequest, + clienttoken_http::{ + ChallengeAnswer, ChallengeType, ClientTokenRequest, ClientTokenRequestType, + ClientTokenResponse, ClientTokenResponseType, + }, + connect::PutStateRequest, + context::Context, + extended_metadata::BatchedEntityRequest, + extended_metadata::{BatchedExtensionResponse, EntityRequest, ExtensionQuery}, + extension_kind::ExtensionKind, + }, + token::Token, + util, + version::spotify_semantic_version, +}; +use bytes::Bytes; +use data_encoding::HEXUPPER_PERMISSIVE; +use futures_util::future::IntoStream; +use http::{Uri, header::HeaderValue}; +use hyper::{ + HeaderMap, Method, Request, + header::{ACCEPT, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, HeaderName, RANGE}, +}; +use hyper_util::client::legacy::ResponseFuture; +use protobuf::{Enum, EnumOrUnknown, Message, MessageFull}; +use rand::RngCore; +use serde::Serialize; +use sysinfo::System; +use thiserror::Error; + +component! { + SpClient : SpClientInner { + accesspoint: Option = None, + strategy: RequestStrategy = RequestStrategy::default(), + client_token: Option = None, + } +} + +pub type SpClientResult = Result; + +#[allow(clippy::declare_interior_mutable_const)] +pub const CLIENT_TOKEN: HeaderName = HeaderName::from_static("client-token"); +#[allow(clippy::declare_interior_mutable_const)] +const CONNECTION_ID: HeaderName = HeaderName::from_static("x-spotify-connection-id"); + +const NO_METRICS_AND_SALT: RequestOptions = RequestOptions { + metrics: false, + salt: false, + base_url: None, +}; + +#[derive(Debug, Error)] +pub enum SpClientError { + #[error("missing attribute {0}")] + Attribute(String), + #[error("expected data but received none")] + NoData, + #[error("expected an entry to exist in {0}")] + ExpectedEntry(&'static str), +} + +impl From for Error { + fn from(err: SpClientError) -> Self { + Self::failed_precondition(err) + } +} + +#[derive(Copy, Clone, Debug)] +pub enum RequestStrategy { + TryTimes(usize), + Infinitely, +} + +impl Default for RequestStrategy { + fn default() -> Self { + RequestStrategy::TryTimes(10) + } +} + +pub struct RequestOptions { + metrics: bool, + salt: bool, + base_url: Option<&'static str>, +} + +impl Default for RequestOptions { + fn default() -> Self { + Self { + metrics: true, + salt: true, + base_url: None, + } + } +} + +#[derive(Debug, Serialize)] +pub struct TransferRequest { + pub transfer_options: TransferOptions, +} + +impl SpClient { + pub fn set_strategy(&self, strategy: RequestStrategy) { + self.lock(|inner| inner.strategy = strategy) + } + + pub async fn flush_accesspoint(&self) { + self.lock(|inner| inner.accesspoint = None) + } + + pub async fn get_accesspoint(&self) -> Result { + // Memoize the current access point. + let ap = self.lock(|inner| inner.accesspoint.clone()); + let tuple = match ap { + Some(tuple) => tuple, + None => { + let tuple = self.session().apresolver().resolve("spclient").await?; + self.lock(|inner| inner.accesspoint = Some(tuple.clone())); + info!( + "Resolved \"{}:{}\" as spclient access point", + tuple.0, tuple.1 + ); + tuple + } + }; + Ok(tuple) + } + + pub async fn base_url(&self) -> Result { + let ap = self.get_accesspoint().await?; + Ok(format!("https://{}:{}", ap.0, ap.1)) + } + + async fn client_token_request(&self, message: &M) -> Result { + let body = message.write_to_bytes()?; + + let request = Request::builder() + .method(&Method::POST) + .uri("https://clienttoken.spotify.com/v1/clienttoken") + .header(ACCEPT, HeaderValue::from_static("application/x-protobuf")) + .body(body.into())?; + + self.session().http_client().request_body(request).await + } + + pub async fn client_token(&self) -> Result { + let client_token = self.lock(|inner| { + if let Some(token) = &inner.client_token { + if token.is_expired() { + inner.client_token = None; + } + } + inner.client_token.clone() + }); + + if let Some(client_token) = client_token { + return Ok(client_token.access_token); + } + + debug!("Client token unavailable or expired, requesting new token."); + + let mut request = ClientTokenRequest::new(); + request.request_type = ClientTokenRequestType::REQUEST_CLIENT_DATA_REQUEST.into(); + + let client_data = request.mut_client_data(); + + client_data.client_version = spotify_semantic_version(); + + // Current state of affairs: keymaster ID works on all tested platforms, but may be phased out, + // so it seems a good idea to mimick the real clients. `self.session().client_id()` returns the + // ID of the client that last connected, but requesting a client token with this ID only works + // on macOS and Windows. On Android and iOS we can send a platform-specific client ID and are + // then presented with a hash cash challenge. On Linux, we have to pass the old keymaster ID. + // We delegate most of this logic to `SessionConfig`. + let os = OS; + let client_id = match os { + "macos" | "windows" => self.session().client_id(), + os => SessionConfig::default_for_os(os).client_id, + }; + client_data.client_id = client_id; + + let connectivity_data = client_data.mut_connectivity_sdk_data(); + connectivity_data.device_id = self.session().device_id().to_string(); + + let platform_data = connectivity_data + .platform_specific_data + .mut_or_insert_default(); + + let os_version = os_version(); + let kernel_version = System::kernel_version().unwrap_or_else(|| String::from("0")); + + match os { + "windows" => { + let os_version = os_version.parse::().unwrap_or(10.) as i32; + let kernel_version = kernel_version.parse::().unwrap_or(21370); + + let (pe, image_file) = match std::env::consts::ARCH { + "arm" => (448, 452), + "aarch64" => (43620, 452), + "x86_64" => (34404, 34404), + _ => (332, 332), // x86 + }; + + let windows_data = platform_data.mut_desktop_windows(); + windows_data.os_version = os_version; + windows_data.os_build = kernel_version; + windows_data.platform_id = 2; + windows_data.unknown_value_6 = 9; + windows_data.image_file_machine = image_file; + windows_data.pe_machine = pe; + windows_data.unknown_value_10 = true; + } + "ios" => { + let ios_data = platform_data.mut_ios(); + ios_data.user_interface_idiom = 0; + ios_data.target_iphone_simulator = false; + ios_data.hw_machine = "iPhone14,5".to_string(); + ios_data.system_version = os_version; + } + "android" => { + let android_data = platform_data.mut_android(); + android_data.android_version = os_version; + android_data.api_version = 31; + "Pixel".clone_into(&mut android_data.device_name); + "GF5KQ".clone_into(&mut android_data.model_str); + "Google".clone_into(&mut android_data.vendor); + } + "macos" => { + let macos_data = platform_data.mut_desktop_macos(); + macos_data.system_version = os_version; + macos_data.hw_model = "iMac21,1".to_string(); + macos_data.compiled_cpu_type = std::env::consts::ARCH.to_string(); + } + _ => { + let linux_data = platform_data.mut_desktop_linux(); + linux_data.system_name = "Linux".to_string(); + linux_data.system_release = kernel_version; + linux_data.system_version = os_version; + linux_data.hardware = std::env::consts::ARCH.to_string(); + } + } + + let mut response = self.client_token_request(&request).await?; + let mut count = 0; + const MAX_TRIES: u8 = 3; + + let token_response = loop { + count += 1; + + let message = ClientTokenResponse::parse_from_bytes(&response)?; + + match ClientTokenResponseType::from_i32(message.response_type.value()) { + // depending on the platform, you're either given a token immediately + // or are presented a hash cash challenge to solve first + Some(ClientTokenResponseType::RESPONSE_GRANTED_TOKEN_RESPONSE) => { + debug!("Received a granted token"); + break message; + } + Some(ClientTokenResponseType::RESPONSE_CHALLENGES_RESPONSE) => { + debug!("Received a hash cash challenge, solving..."); + + let challenges = message.challenges().clone(); + let state = challenges.state; + if let Some(challenge) = challenges.challenges.first() { + let hash_cash_challenge = challenge.evaluate_hashcash_parameters(); + + let ctx = vec![]; + let prefix = HEXUPPER_PERMISSIVE + .decode(hash_cash_challenge.prefix.as_bytes()) + .map_err(|e| { + Error::failed_precondition(format!( + "Unable to decode hash cash challenge: {e}" + )) + })?; + let length = hash_cash_challenge.length; + + let mut suffix = [0u8; 0x10]; + let answer = util::solve_hash_cash(&ctx, &prefix, length, &mut suffix); + + match answer { + Ok(_) => { + // the suffix must be in uppercase + let suffix = HEXUPPER_PERMISSIVE.encode(&suffix); + + let mut answer_message = ClientTokenRequest::new(); + answer_message.request_type = + ClientTokenRequestType::REQUEST_CHALLENGE_ANSWERS_REQUEST + .into(); + + let challenge_answers = answer_message.mut_challenge_answers(); + + let mut challenge_answer = ChallengeAnswer::new(); + challenge_answer.mut_hash_cash().suffix = suffix; + challenge_answer.ChallengeType = + ChallengeType::CHALLENGE_HASH_CASH.into(); + + challenge_answers.state = state.to_string(); + challenge_answers.answers.push(challenge_answer); + + trace!("Answering hash cash challenge"); + match self.client_token_request(&answer_message).await { + Ok(token) => { + response = token; + continue; + } + Err(e) => { + trace!("Answer not accepted {count}/{MAX_TRIES}: {e}"); + } + } + } + Err(e) => trace!( + "Unable to solve hash cash challenge {count}/{MAX_TRIES}: {e}" + ), + } + + if count < MAX_TRIES { + response = self.client_token_request(&request).await?; + } else { + return Err(Error::failed_precondition(format!( + "Unable to solve any of {MAX_TRIES} hash cash challenges" + ))); + } + } else { + return Err(Error::failed_precondition("No challenges found")); + } + } + + Some(unknown) => { + return Err(Error::unimplemented(format!( + "Unknown client token response type: {unknown:?}" + ))); + } + None => return Err(Error::failed_precondition("No client token response type")), + } + }; + + let granted_token = token_response.granted_token(); + let access_token = granted_token.token.to_owned(); + + self.lock(|inner| { + let client_token = Token { + access_token: access_token.clone(), + expires_in: Duration::from_secs( + granted_token + .refresh_after_seconds + .try_into() + .unwrap_or(7200), + ), + token_type: "client-token".to_string(), + scopes: granted_token + .domains + .iter() + .map(|d| d.domain.clone()) + .collect(), + timestamp: SystemTime::now(), + }; + + inner.client_token = Some(client_token); + }); + + trace!("Got client token: {granted_token:?}"); + + Ok(access_token) + } + + pub async fn request_with_protobuf( + &self, + method: &Method, + endpoint: &str, + headers: Option, + message: &M, + ) -> SpClientResult { + self.request_with_protobuf_and_options( + method, + endpoint, + headers, + message, + &Default::default(), + ) + .await + } + + pub async fn request_with_protobuf_and_options( + &self, + method: &Method, + endpoint: &str, + headers: Option, + message: &M, + options: &RequestOptions, + ) -> SpClientResult { + let body = message.write_to_bytes()?; + + let mut headers = headers.unwrap_or_default(); + headers.insert( + CONTENT_TYPE, + HeaderValue::from_static("application/x-protobuf"), + ); + + self.request_with_options(method, endpoint, Some(headers), Some(&body), options) + .await + } + + pub async fn request_as_json( + &self, + method: &Method, + endpoint: &str, + headers: Option, + body: Option<&str>, + ) -> SpClientResult { + let mut headers = headers.unwrap_or_default(); + headers.insert(ACCEPT, HeaderValue::from_static("application/json")); + + self.request(method, endpoint, Some(headers), body.map(|s| s.as_bytes())) + .await + } + + pub async fn request( + &self, + method: &Method, + endpoint: &str, + headers: Option, + body: Option<&[u8]>, + ) -> SpClientResult { + self.request_with_options(method, endpoint, headers, body, &Default::default()) + .await + } + + pub async fn request_with_options( + &self, + method: &Method, + endpoint: &str, + headers: Option, + body: Option<&[u8]>, + options: &RequestOptions, + ) -> SpClientResult { + let mut tries: usize = 0; + let mut last_response; + + let body = body.unwrap_or_default(); + + loop { + tries += 1; + + // Reconnection logic: retrieve the endpoint every iteration, so we can try + // another access point when we are experiencing network issues (see below). + let mut url = match options.base_url { + Some(base_url) => base_url.to_string(), + None => self.base_url().await?, + }; + url.push_str(endpoint); + + // Add metrics. There is also an optional `partner` key with a value like + // `vodafone-uk` but we've yet to discover how we can find that value. + // For the sake of documentation you could also do "product=free" but + // we only support premium anyway. + if options.metrics && !url.contains("product=0") { + let _ = write!( + url, + "{}product=0&country={}", + util::get_next_query_separator(&url), + self.session().country() + ); + } + + // Defeat caches. Spotify-generated URLs already contain this. + if options.salt && !url.contains("salt=") { + let _ = write!( + url, + "{}salt={}", + util::get_next_query_separator(&url), + rand::rng().next_u32() + ); + } + + let mut request = Request::builder() + .method(method) + .uri(url) + .header(CONTENT_LENGTH, body.len()) + .body(Bytes::copy_from_slice(body))?; + + // Reconnection logic: keep getting (cached) tokens because they might have expired. + let token = self.session().login5().auth_token().await?; + + let headers_mut = request.headers_mut(); + if let Some(ref headers) = headers { + for (name, value) in headers { + headers_mut.insert(name, value.clone()); + } + } + + headers_mut.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("{} {}", token.token_type, token.access_token,))?, + ); + + match self.client_token().await { + Ok(client_token) => { + let _ = headers_mut.insert(CLIENT_TOKEN, HeaderValue::from_str(&client_token)?); + } + Err(e) => { + // currently these endpoints seem to work fine without it + warn!("Unable to get client token: {e} Trying to continue without...") + } + } + + last_response = self.session().http_client().request_body(request).await; + + if last_response.is_ok() { + return last_response; + } + + // Break before the reconnection logic below, so that the current access point + // is retained when max_tries == 1. Leave it up to the caller when to flush. + if let RequestStrategy::TryTimes(max_tries) = self.lock(|inner| inner.strategy) { + if tries >= max_tries { + break; + } + } + + // Reconnection logic: drop the current access point if we are experiencing issues. + // This will cause the next call to base_url() to resolve a new one. + if let Err(ref network_error) = last_response { + match network_error.kind { + ErrorKind::Unavailable | ErrorKind::DeadlineExceeded => { + // Keep trying the current access point three times before dropping it. + if tries % 3 == 0 { + self.flush_accesspoint().await + } + } + _ => break, // if we can't build the request now, then we won't ever + } + } + + debug!("Error was: {last_response:?}"); + } + + last_response + } + + pub async fn put_connect_state_request(&self, state: &PutStateRequest) -> SpClientResult { + let endpoint = format!("/connect-state/v1/devices/{}", self.session().device_id()); + + let mut headers = HeaderMap::new(); + headers.insert(CONNECTION_ID, self.session().connection_id().parse()?); + + self.request_with_protobuf(&Method::PUT, &endpoint, Some(headers), state) + .await + } + + pub async fn delete_connect_state_request(&self) -> SpClientResult { + let endpoint = format!("/connect-state/v1/devices/{}", self.session().device_id()); + self.request(&Method::DELETE, &endpoint, None, None).await + } + + pub async fn put_connect_state_inactive(&self, notify: bool) -> SpClientResult { + let endpoint = format!( + "/connect-state/v1/devices/{}/inactive?notify={notify}", + self.session().device_id() + ); + + let mut headers = HeaderMap::new(); + headers.insert(CONNECTION_ID, self.session().connection_id().parse()?); + + self.request(&Method::PUT, &endpoint, Some(headers), None) + .await + } + + pub async fn get_extended_metadata( + &self, + request: BatchedEntityRequest, + ) -> Result { + let res = self + .request_with_protobuf( + &Method::POST, + "/extended-metadata/v0/extended-metadata", + None, + &request, + ) + .await?; + Ok(BatchedExtensionResponse::parse_from_bytes(&res)?) + } + + pub async fn get_metadata(&self, kind: ExtensionKind, id: &SpotifyUri) -> SpClientResult { + let req = BatchedEntityRequest { + entity_request: vec![EntityRequest { + entity_uri: id.to_uri()?, + query: vec![ExtensionQuery { + extension_kind: EnumOrUnknown::new(kind), + ..Default::default() + }], + ..Default::default() + }], + ..Default::default() + }; + + let mut res = self.get_extended_metadata(req).await?; + let mut extended_metadata = res + .extended_metadata + .pop() + .ok_or(SpClientError::ExpectedEntry("extended_metadata"))?; + + let mut data = extended_metadata + .extension_data + .pop() + .ok_or(SpClientError::ExpectedEntry("extension_data"))?; + + match data.extension_data.take() { + None => Err(SpClientError::ExpectedEntry("data").into()), + Some(data) => Ok(Bytes::from(data.value)), + } + } + + pub async fn get_track_metadata(&self, track_uri: &SpotifyUri) -> SpClientResult { + self.get_metadata(ExtensionKind::TRACK_V4, track_uri).await + } + + pub async fn get_episode_metadata(&self, episode_uri: &SpotifyUri) -> SpClientResult { + self.get_metadata(ExtensionKind::EPISODE_V4, episode_uri) + .await + } + + pub async fn get_album_metadata(&self, album_uri: &SpotifyUri) -> SpClientResult { + self.get_metadata(ExtensionKind::ALBUM_V4, album_uri).await + } + + pub async fn get_artist_metadata(&self, artist_uri: &SpotifyUri) -> SpClientResult { + self.get_metadata(ExtensionKind::ARTIST_V4, artist_uri) + .await + } + + pub async fn get_show_metadata(&self, show_uri: &SpotifyUri) -> SpClientResult { + self.get_metadata(ExtensionKind::SHOW_V4, show_uri).await + } + + pub async fn get_lyrics(&self, track_id: &SpotifyId) -> SpClientResult { + let endpoint = format!("/color-lyrics/v2/track/{}", track_id.to_base62()?); + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_lyrics_for_image( + &self, + track_id: &SpotifyId, + image_id: &FileId, + ) -> SpClientResult { + let endpoint = format!( + "/color-lyrics/v2/track/{}/image/spotify:image:{}", + track_id.to_base62()?, + image_id + ); + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_playlist(&self, playlist_id: &SpotifyId) -> SpClientResult { + let endpoint = format!("/playlist/v2/playlist/{}", playlist_id.to_base62()?); + + self.request(&Method::GET, &endpoint, None, None).await + } + + pub async fn get_user_profile( + &self, + username: &str, + playlist_limit: Option, + artist_limit: Option, + ) -> SpClientResult { + let mut endpoint = format!("/user-profile-view/v3/profile/{username}"); + + if playlist_limit.is_some() || artist_limit.is_some() { + let _ = write!(endpoint, "?"); + + if let Some(limit) = playlist_limit { + let _ = write!(endpoint, "playlist_limit={limit}"); + if artist_limit.is_some() { + let _ = write!(endpoint, "&"); + } + } + + if let Some(limit) = artist_limit { + let _ = write!(endpoint, "artist_limit={limit}"); + } + } + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_user_followers(&self, username: &str) -> SpClientResult { + let endpoint = format!("/user-profile-view/v3/profile/{username}/followers"); + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_user_following(&self, username: &str) -> SpClientResult { + let endpoint = format!("/user-profile-view/v3/profile/{username}/following"); + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_radio_for_track(&self, track_uri: &SpotifyUri) -> SpClientResult { + let endpoint = format!( + "/inspiredby-mix/v2/seed_to_playlist/{}?response-format=json", + track_uri.to_uri()? + ); + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + // Known working scopes: stations, tracks + // For others see: https://gist.github.com/roderickvd/62df5b74d2179a12de6817a37bb474f9 + // + // Seen-in-the-wild but unimplemented query parameters: + // - image_style=gradient_overlay + // - excludeClusters=true + // - language=en + // - count_tracks=0 + // - market=from_token + pub async fn get_apollo_station( + &self, + scope: &str, + context_uri: &str, + count: Option, + previous_tracks: Vec, + autoplay: bool, + ) -> SpClientResult { + let mut endpoint = format!("/radio-apollo/v3/{scope}/{context_uri}?autoplay={autoplay}"); + + // Spotify has a default of 50 + if let Some(count) = count { + let _ = write!(endpoint, "&count={count}"); + } + + let previous_track_str = previous_tracks + .iter() + .map(|track| track.to_base62()) + .collect::, _>>()? + .join(","); + // better than checking `previous_tracks.len() > 0` because the `filter_map` could still return 0 items + if !previous_track_str.is_empty() { + let _ = write!(endpoint, "&prev_tracks={previous_track_str}"); + } + + self.request_as_json(&Method::GET, &endpoint, None, None) + .await + } + + pub async fn get_next_page(&self, next_page_uri: &str) -> SpClientResult { + let endpoint = next_page_uri.trim_start_matches("hm:/"); + self.request_as_json(&Method::GET, endpoint, None, None) + .await + } + + // TODO: Seen-in-the-wild but unimplemented endpoints + // - /presence-view/v1/buddylist + + pub async fn get_audio_storage(&self, file_id: &FileId) -> SpClientResult { + let endpoint = format!( + "/storage-resolve/files/audio/interactive/{}", + file_id.to_base16()? + ); + self.request(&Method::GET, &endpoint, None, None).await + } + + pub fn stream_from_cdn( + &self, + cdn_url: U, + offset: usize, + length: usize, + ) -> Result, Error> + where + U: TryInto, + >::Error: Into, + { + let req = Request::builder() + .method(&Method::GET) + .uri(cdn_url) + .header( + RANGE, + HeaderValue::from_str(&format!("bytes={}-{}", offset, offset + length - 1))?, + ) + .body(Bytes::new())?; + + let stream = self.session().http_client().request_stream(req)?; + + Ok(stream) + } + + pub async fn request_url(&self, url: &str) -> SpClientResult { + let request = Request::builder() + .method(&Method::GET) + .uri(url) + .body(Bytes::new())?; + + self.session().http_client().request_body(request).await + } + + // Audio preview in 96 kbps MP3, unencrypted + pub async fn get_audio_preview(&self, preview_id: &FileId) -> SpClientResult { + const ATTRIBUTE: &str = "audio-preview-url-template"; + let template = self + .session() + .get_user_attribute(ATTRIBUTE) + .ok_or_else(|| SpClientError::Attribute(ATTRIBUTE.to_string()))?; + + let mut url = template.replace("{id}", &preview_id.to_base16()?); + let separator = match url.find('?') { + Some(_) => "&", + None => "?", + }; + let _ = write!(url, "{}cid={}", separator, self.session().client_id()); + + self.request_url(&url).await + } + + // The first 128 kB of a track, unencrypted + pub async fn get_head_file(&self, file_id: &FileId) -> SpClientResult { + const ATTRIBUTE: &str = "head-files-url"; + let template = self + .session() + .get_user_attribute(ATTRIBUTE) + .ok_or_else(|| SpClientError::Attribute(ATTRIBUTE.to_string()))?; + + let url = template.replace("{file_id}", &file_id.to_base16()?); + + self.request_url(&url).await + } + + pub async fn get_image(&self, image_id: &FileId) -> SpClientResult { + const ATTRIBUTE: &str = "image-url"; + let template = self + .session() + .get_user_attribute(ATTRIBUTE) + .ok_or_else(|| SpClientError::Attribute(ATTRIBUTE.to_string()))?; + let url = template.replace("{file_id}", &image_id.to_base16()?); + + self.request_url(&url).await + } + + /// Request the context for an uri + /// + /// All [SpotifyId] uris are supported in addition to the following special uris: + /// - liked songs: + /// - all: `spotify:user::collection` + /// - of artist: `spotify:user::collection:artist:` + /// - search: `spotify:search:` (whitespaces are replaced with `+`) + /// + /// ## Query params found in the wild: + /// - include_video=true + /// + /// ## Known results of uri types: + /// - uris of type `track` + /// - returns a single page with a single track + /// - when requesting a single track with a query in the request, the returned track uri + /// **will** contain the query + /// - uris of type `artist` + /// - returns 2 pages with tracks: 10 most popular tracks and latest/popular album + /// - remaining pages are artist albums sorted by popularity (only provided as page_url) + /// - uris of type `search` + /// - is massively influenced by the provided query + /// - the query result shown by the search expects no query at all + /// - uri looks like `spotify:search:never+gonna` + pub async fn get_context(&self, uri: &str) -> Result { + let uri = format!("/context-resolve/v1/{uri}"); + + let res = self + .request_with_options(&Method::GET, &uri, None, None, &NO_METRICS_AND_SALT) + .await?; + let ctx_json = String::from_utf8(res.to_vec())?; + if ctx_json.is_empty() { + Err(SpClientError::NoData)? + } + + let ctx = protobuf_json_mapping::parse_from_str::(&ctx_json); + + if ctx.is_err() { + trace!("failed parsing context: {ctx_json}") + } + + Ok(ctx?) + } + + pub async fn get_autoplay_context( + &self, + context_request: &AutoplayContextRequest, + ) -> Result { + let res = self + .request_with_protobuf_and_options( + &Method::POST, + "/context-resolve/v1/autoplay", + None, + context_request, + &NO_METRICS_AND_SALT, + ) + .await?; + + let ctx_json = String::from_utf8(res.to_vec())?; + if ctx_json.is_empty() { + Err(SpClientError::NoData)? + } + + let ctx = protobuf_json_mapping::parse_from_str::(&ctx_json); + + if ctx.is_err() { + trace!("failed parsing context: {ctx_json}") + } + + Ok(ctx?) + } + + pub async fn get_rootlist(&self, from: usize, length: Option) -> SpClientResult { + let length = length.unwrap_or(120); + let user = self.session().username(); + let endpoint = format!( + "/playlist/v2/user/{user}/rootlist?decorate=revision,attributes,length,owner,capabilities,status_code&from={from}&length={length}" + ); + + self.request(&Method::GET, &endpoint, None, None).await + } + + /// Triggers the transfers of the playback from one device to another + /// + /// Using the same `device_id` for `from_device_id` and `to_device_id`, initiates the transfer + /// from the currently active device. + pub async fn transfer( + &self, + from_device_id: &str, + to_device_id: &str, + transfer_request: Option<&TransferRequest>, + ) -> SpClientResult { + let body = transfer_request.map(serde_json::to_string).transpose()?; + + let endpoint = + format!("/connect-state/v1/connect/transfer/from/{from_device_id}/to/{to_device_id}"); + self.request_with_options( + &Method::POST, + &endpoint, + None, + body.as_deref().map(|s| s.as_bytes()), + &NO_METRICS_AND_SALT, + ) + .await + } +} diff --git a/native/vendor/librespot-core/src/spotify_id.rs b/native/vendor/librespot-core/src/spotify_id.rs new file mode 100644 index 00000000..c627b551 --- /dev/null +++ b/native/vendor/librespot-core/src/spotify_id.rs @@ -0,0 +1,387 @@ +use std::fmt; + +use thiserror::Error; + +use crate::{Error, SpotifyUri}; + +// re-export FileId for historic reasons, when it was part of this mod +pub use crate::FileId; + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct SpotifyId { + pub id: u128, +} + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] +pub enum SpotifyIdError { + #[error("ID cannot be parsed")] + InvalidId, + #[error("not a valid Spotify ID")] + InvalidFormat, +} + +impl From for Error { + fn from(err: SpotifyIdError) -> Self { + Error::invalid_argument(err) + } +} + +pub type SpotifyIdResult = Result; + +const BASE62_DIGITS: &[u8; 62] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const BASE16_DIGITS: &[u8; 16] = b"0123456789abcdef"; + +impl SpotifyId { + const SIZE: usize = 16; + const SIZE_BASE16: usize = 32; + const SIZE_BASE62: usize = 22; + + /// Parses a base16 (hex) encoded [Spotify ID] into a `SpotifyId`. + /// + /// `src` is expected to be 32 bytes long and encoded using valid characters. + /// + /// [Spotify ID]: https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids + pub fn from_base16(src: &str) -> SpotifyIdResult { + if src.len() != 32 { + return Err(SpotifyIdError::InvalidId.into()); + } + let mut dst: u128 = 0; + + for c in src.as_bytes() { + let p = match c { + b'0'..=b'9' => c - b'0', + b'a'..=b'f' => c - b'a' + 10, + _ => return Err(SpotifyIdError::InvalidId.into()), + } as u128; + + dst <<= 4; + dst += p; + } + + Ok(Self { id: dst }) + } + + /// Parses a base62 encoded [Spotify ID] into a `u128`. + /// + /// `src` is expected to be 22 bytes long and encoded using valid characters. + /// + /// [Spotify ID]: https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids + pub fn from_base62(src: &str) -> SpotifyIdResult { + if src.len() != Self::SIZE_BASE62 { + return Err(SpotifyIdError::InvalidId.into()); + } + let mut dst: u128 = 0; + + for c in src.as_bytes() { + let p = match c { + b'0'..=b'9' => c - b'0', + b'a'..=b'z' => c - b'a' + 10, + b'A'..=b'Z' => c - b'A' + 36, + _ => return Err(SpotifyIdError::InvalidId.into()), + } as u128; + + dst = dst.checked_mul(62).ok_or(SpotifyIdError::InvalidId)?; + dst = dst.checked_add(p).ok_or(SpotifyIdError::InvalidId)?; + } + + Ok(Self { id: dst }) + } + + /// Creates a `u128` from a copy of `SpotifyId::SIZE` (16) bytes in big-endian order. + /// + /// The resulting `SpotifyId` will default to a `SpotifyItemType::Unknown`. + pub fn from_raw(src: &[u8]) -> SpotifyIdResult { + match src.try_into() { + Ok(dst) => Ok(Self { + id: u128::from_be_bytes(dst), + }), + Err(_) => Err(SpotifyIdError::InvalidId.into()), + } + } + + /// Returns the `SpotifyId` as a base16 (hex) encoded, `SpotifyId::SIZE_BASE16` (32) + /// character long `String`. + #[allow(clippy::wrong_self_convention)] + pub fn to_base16(&self) -> Result { + to_base16(&self.to_raw(), &mut [0u8; Self::SIZE_BASE16]) + } + + /// Returns the `SpotifyId` as a [canonically] base62 encoded, `SpotifyId::SIZE_BASE62` (22) + /// character long `String`. + /// + /// [canonically]: https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids + #[allow(clippy::wrong_self_convention)] + pub fn to_base62(&self) -> Result { + let mut dst = [0u8; 22]; + let mut i = 0; + let n = self.id; + + // The algorithm is based on: + // https://github.com/trezor/trezor-crypto/blob/c316e775a2152db255ace96b6b65ac0f20525ec0/base58.c + // + // We are not using naive division of self.id as it is an u128 and div + mod are software + // emulated at runtime (and unoptimized into mul + shift) on non-128bit platforms, + // making them very expensive. + // + // Trezor's algorithm allows us to stick to arithmetic on native registers making this + // an order of magnitude faster. Additionally, as our sizes are known, instead of + // dealing with the ID on a byte by byte basis, we decompose it into four u32s and + // use 64-bit arithmetic on them for an additional speedup. + for shift in &[96, 64, 32, 0] { + let mut carry = (n >> shift) as u32 as u64; + + for b in &mut dst[..i] { + carry += (*b as u64) << 32; + *b = (carry % 62) as u8; + carry /= 62; + } + + while carry > 0 { + dst[i] = (carry % 62) as u8; + carry /= 62; + i += 1; + } + } + + for b in &mut dst { + *b = BASE62_DIGITS[*b as usize]; + } + + dst.reverse(); + + String::from_utf8(dst.to_vec()).map_err(|_| SpotifyIdError::InvalidId.into()) + } + + /// Returns a copy of the `SpotifyId` as an array of `SpotifyId::SIZE` (16) bytes in + /// big-endian order. + #[allow(clippy::wrong_self_convention)] + pub fn to_raw(&self) -> [u8; Self::SIZE] { + self.id.to_be_bytes() + } +} + +impl fmt::Debug for SpotifyId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SpotifyId") + .field(&self.to_base62().unwrap_or_else(|_| "invalid uri".into())) + .finish() + } +} + +impl fmt::Display for SpotifyId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_base62().unwrap_or_else(|_| "invalid uri".into())) + } +} + +impl TryFrom<&[u8]> for SpotifyId { + type Error = crate::Error; + fn try_from(src: &[u8]) -> Result { + Self::from_raw(src) + } +} + +impl TryFrom<&str> for SpotifyId { + type Error = crate::Error; + fn try_from(src: &str) -> Result { + Self::from_base62(src) + } +} + +impl TryFrom for SpotifyId { + type Error = crate::Error; + fn try_from(src: String) -> Result { + Self::try_from(src.as_str()) + } +} + +impl TryFrom<&Vec> for SpotifyId { + type Error = crate::Error; + fn try_from(src: &Vec) -> Result { + Self::try_from(src.as_slice()) + } +} + +impl TryFrom<&SpotifyUri> for SpotifyId { + type Error = crate::Error; + fn try_from(value: &SpotifyUri) -> Result { + match value { + SpotifyUri::Album { id } + | SpotifyUri::Artist { id } + | SpotifyUri::Episode { id } + | SpotifyUri::Playlist { id, .. } + | SpotifyUri::Show { id } + | SpotifyUri::Track { id } => Ok(*id), + SpotifyUri::Local { .. } | SpotifyUri::Unknown { .. } => { + Err(SpotifyIdError::InvalidFormat.into()) + } + } + } +} + +pub fn to_base16(src: &[u8], buf: &mut [u8]) -> Result { + let mut i = 0; + for v in src { + buf[i] = BASE16_DIGITS[(v >> 4) as usize]; + buf[i + 1] = BASE16_DIGITS[(v & 0x0f) as usize]; + i += 2; + } + + String::from_utf8(buf.to_vec()).map_err(|_| SpotifyIdError::InvalidId.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + struct ConversionCase { + id: u128, + base16: &'static str, + base62: &'static str, + raw: &'static [u8], + } + + static CONV_VALID: [ConversionCase; 5] = [ + ConversionCase { + id: 238762092608182713602505436543891614649, + base16: "b39fe8081e1f4c54be38e8d6f9f12bb9", + base62: "5sWHDYs0csV6RS48xBl0tH", + raw: &[ + 179, 159, 232, 8, 30, 31, 76, 84, 190, 56, 232, 214, 249, 241, 43, 185, + ], + }, + ConversionCase { + id: 204841891221366092811751085145916697048, + base16: "9a1b1cfbc6f244569ae0356c77bbe9d8", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + raw: &[ + 154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216, + ], + }, + ConversionCase { + id: 204841891221366092811751085145916697048, + base16: "9a1b1cfbc6f244569ae0356c77bbe9d8", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + raw: &[ + 154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216, + ], + }, + ConversionCase { + id: 204841891221366092811751085145916697048, + base16: "9a1b1cfbc6f244569ae0356c77bbe9d8", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + raw: &[ + 154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 53, 108, 119, 187, 233, 216, + ], + }, + ConversionCase { + id: 0, + base16: "00000000000000000000000000000000", + base62: "0000000000000000000000", + raw: &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + }, + ]; + + static CONV_INVALID: [ConversionCase; 5] = [ + ConversionCase { + id: 0, + base16: "ZZZZZ8081e1f4c54be38e8d6f9f12bb9", + base62: "!!!!!Ys0csV6RS48xBl0tH", + raw: &[ + // Invalid length. + 154, 27, 28, 251, 198, 242, 68, 86, 154, 224, 5, 3, 108, 119, 187, 233, 216, 255, + ], + }, + ConversionCase { + id: 0, + base16: "--------------------", + base62: "....................", + raw: &[ + // Invalid length. + 154, 27, 28, 251, + ], + }, + ConversionCase { + id: 0, + // too long, should return error but not panic overflow + base16: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + // too long, should return error but not panic overflow + base62: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + raw: &[ + // Invalid length. + 154, 27, 28, 251, + ], + }, + ConversionCase { + id: 0, + base16: "--------------------", + // too short to encode a 128 bits int + base62: "aa", + raw: &[ + // Invalid length. + 154, 27, 28, 251, + ], + }, + ConversionCase { + id: 0, + base16: "--------------------", + // too high of a value, this would need a 132 bits int + base62: "ZZZZZZZZZZZZZZZZZZZZZZ", + raw: &[ + // Invalid length. + 154, 27, 28, 251, + ], + }, + ]; + + #[test] + fn from_base62() { + for c in &CONV_VALID { + assert_eq!(SpotifyId::from_base62(c.base62).unwrap().id, c.id); + } + + for c in &CONV_INVALID { + assert!(SpotifyId::from_base62(c.base62).is_err(),); + } + } + + #[test] + fn to_base62() { + for c in &CONV_VALID { + let id = SpotifyId { id: c.id }; + + assert_eq!(id.to_base62().unwrap(), c.base62); + } + } + + #[test] + fn from_base16() { + for c in &CONV_VALID { + assert_eq!(SpotifyId::from_base16(c.base16).unwrap().id, c.id); + } + + for c in &CONV_INVALID { + assert!(SpotifyId::from_base16(c.base16).is_err(),); + } + } + + #[test] + fn to_base16() { + for c in &CONV_VALID { + let id = SpotifyId { id: c.id }; + + assert_eq!(id.to_base16().unwrap(), c.base16); + } + } + + #[test] + fn from_raw() { + for c in &CONV_VALID { + assert_eq!(SpotifyId::from_raw(c.raw).unwrap().id, c.id); + } + + for c in &CONV_INVALID { + assert!(SpotifyId::from_raw(c.raw).is_err()); + } + } +} diff --git a/native/vendor/librespot-core/src/spotify_uri.rs b/native/vendor/librespot-core/src/spotify_uri.rs new file mode 100644 index 00000000..f92f2d3b --- /dev/null +++ b/native/vendor/librespot-core/src/spotify_uri.rs @@ -0,0 +1,615 @@ +use crate::{Error, SpotifyId}; +use std::{borrow::Cow, fmt, str::FromStr, time::Duration}; +use thiserror::Error; + +use librespot_protocol as protocol; + +const SPOTIFY_ITEM_TYPE_ALBUM: &str = "album"; +const SPOTIFY_ITEM_TYPE_ARTIST: &str = "artist"; +const SPOTIFY_ITEM_TYPE_EPISODE: &str = "episode"; +const SPOTIFY_ITEM_TYPE_PLAYLIST: &str = "playlist"; +const SPOTIFY_ITEM_TYPE_SHOW: &str = "show"; +const SPOTIFY_ITEM_TYPE_TRACK: &str = "track"; +const SPOTIFY_ITEM_TYPE_LOCAL: &str = "local"; +const SPOTIFY_ITEM_TYPE_UNKNOWN: &str = "unknown"; + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] +pub enum SpotifyUriError { + #[error("not a valid Spotify URI")] + InvalidFormat, + #[error("URI does not belong to Spotify")] + InvalidRoot, +} + +impl From for Error { + fn from(err: SpotifyUriError) -> Self { + Error::invalid_argument(err) + } +} + +pub type SpotifyUriResult = Result; + +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum SpotifyUri { + Album { + id: SpotifyId, + }, + Artist { + id: SpotifyId, + }, + Episode { + id: SpotifyId, + }, + Playlist { + user: Option, + id: SpotifyId, + }, + Show { + id: SpotifyId, + }, + Track { + id: SpotifyId, + }, + Local { + artist: String, + album_title: String, + track_title: String, + duration: std::time::Duration, + }, + Unknown { + kind: Cow<'static, str>, + id: String, + }, +} + +impl SpotifyUri { + /// Returns whether this `SpotifyUri` is for a playable audio item, if known. + pub fn is_playable(&self) -> bool { + matches!( + self, + SpotifyUri::Episode { .. } | SpotifyUri::Track { .. } | SpotifyUri::Local { .. } + ) + } + + /// Gets the item type of this URI as a static string + pub fn item_type(&self) -> &'static str { + match &self { + SpotifyUri::Album { .. } => SPOTIFY_ITEM_TYPE_ALBUM, + SpotifyUri::Artist { .. } => SPOTIFY_ITEM_TYPE_ARTIST, + SpotifyUri::Episode { .. } => SPOTIFY_ITEM_TYPE_EPISODE, + SpotifyUri::Playlist { .. } => SPOTIFY_ITEM_TYPE_PLAYLIST, + SpotifyUri::Show { .. } => SPOTIFY_ITEM_TYPE_SHOW, + SpotifyUri::Track { .. } => SPOTIFY_ITEM_TYPE_TRACK, + SpotifyUri::Local { .. } => SPOTIFY_ITEM_TYPE_LOCAL, + SpotifyUri::Unknown { .. } => SPOTIFY_ITEM_TYPE_UNKNOWN, + } + } + + /// Gets the ID of this URI. The resource ID is the component of the URI that identifies + /// the resource after its type label. If `self` is a named ID, the user will be omitted. + pub fn to_id(&self) -> Result { + match &self { + SpotifyUri::Album { id } + | SpotifyUri::Artist { id } + | SpotifyUri::Episode { id } + | SpotifyUri::Playlist { id, .. } + | SpotifyUri::Show { id } + | SpotifyUri::Track { id } => id.to_base62(), + SpotifyUri::Local { + artist, + album_title, + track_title, + duration, + } => { + let duration_secs = duration.as_secs(); + Ok(format!( + "{artist}:{album_title}:{track_title}:{duration_secs}" + )) + } + SpotifyUri::Unknown { id, .. } => Ok(id.clone()), + } + } + + /// Parses a [Spotify URI] into a `SpotifyUri`. + /// + /// `uri` is expected to be in the canonical form `spotify:{type}:{id}`, where `{type}` + /// can be arbitrary while `{id}` is in a format that varies based on the `{type}`: + /// + /// - For most item types, a 22-character long, base62 encoded Spotify ID is expected. + /// - For local files, an arbitrary length string with the fields + /// `{artist}:{album_title}:{track_title}:{duration_in_seconds}` is expected. + /// + /// Spotify URI: https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids + pub fn from_uri(src: &str) -> SpotifyUriResult { + // Basic: `spotify:{type}:{id}` + // Named: `spotify:user:{user}:{type}:{id}` + // Local: `spotify:local:{artist}:{album_title}:{track_title}:{duration_in_seconds}` + let mut parts = src.split(':'); + + let scheme = parts.next().ok_or(SpotifyUriError::InvalidFormat)?; + + if scheme != "spotify" { + return Err(SpotifyUriError::InvalidRoot.into()); + } + + let mut username: Option = None; + + let item_type = { + let next = parts.next().ok_or(SpotifyUriError::InvalidFormat)?; + if next == "user" { + username.replace( + parts + .next() + .ok_or(SpotifyUriError::InvalidFormat)? + .to_owned(), + ); + parts.next().ok_or(SpotifyUriError::InvalidFormat)? + } else { + next + } + }; + + let name = parts.next().ok_or(SpotifyUriError::InvalidFormat)?; + + match item_type { + SPOTIFY_ITEM_TYPE_ALBUM => Ok(Self::Album { + id: SpotifyId::from_base62(name)?, + }), + SPOTIFY_ITEM_TYPE_ARTIST => Ok(Self::Artist { + id: SpotifyId::from_base62(name)?, + }), + SPOTIFY_ITEM_TYPE_EPISODE => Ok(Self::Episode { + id: SpotifyId::from_base62(name)?, + }), + SPOTIFY_ITEM_TYPE_PLAYLIST => Ok(Self::Playlist { + id: SpotifyId::from_base62(name)?, + user: username, + }), + SPOTIFY_ITEM_TYPE_SHOW => Ok(Self::Show { + id: SpotifyId::from_base62(name)?, + }), + SPOTIFY_ITEM_TYPE_TRACK => Ok(Self::Track { + id: SpotifyId::from_base62(name)?, + }), + SPOTIFY_ITEM_TYPE_LOCAL => { + let artist = name; + let album_title = parts.next().ok_or(SpotifyUriError::InvalidFormat)?; + let track_title = parts.next().ok_or(SpotifyUriError::InvalidFormat)?; + let duration_secs = parts + .next() + .and_then(|f| u64::from_str(f).ok()) + .ok_or(SpotifyUriError::InvalidFormat)?; + + Ok(Self::Local { + artist: artist.to_owned(), + album_title: album_title.to_owned(), + track_title: track_title.to_owned(), + duration: Duration::from_secs(duration_secs), + }) + } + _ => Ok(Self::Unknown { + kind: item_type.to_owned().into(), + id: name.to_owned(), + }), + } + } + + /// Returns the `SpotifyUri` as a [Spotify URI] in the canonical form `spotify:{type}:{id}`, + /// where `{type}` is an arbitrary string and `{id}` is a 22-character long, base62 encoded + /// Spotify ID. + /// + /// If the `SpotifyUri` has an associated type unrecognized by the library, `{type}` will + /// be encoded as `unknown`. + /// + /// If the `SpotifyUri` is named, it will be returned in the form + /// `spotify:user:{user}:{type}:{id}`. + /// + /// [Spotify URI]: https://developer.spotify.com/documentation/web-api/concepts/spotify-uris-ids + pub fn to_uri(&self) -> Result { + let item_type = self.item_type(); + let name = self.to_id()?; + + if let SpotifyUri::Playlist { + id, + user: Some(user), + } = self + { + Ok(format!("spotify:user:{user}:{item_type}:{id}")) + } else { + Ok(format!("spotify:{item_type}:{name}")) + } + } + + /// Gets the name of this URI. The resource name is the component of the URI that identifies + /// the resource after its type label. If `self` is a named ID, the user will be omitted. + /// + /// Deprecated: not all IDs can be represented in Base62, so this function has been renamed to + /// [SpotifyUri::to_id], which this implementation forwards to. + #[deprecated(since = "0.8.0", note = "use to_name instead")] + pub fn to_base62(&self) -> Result { + self.to_id() + } +} + +impl fmt::Debug for SpotifyUri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SpotifyUri") + .field(&self.to_uri().unwrap_or_else(|_| "invalid uri".into())) + .finish() + } +} + +impl fmt::Display for SpotifyUri { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.to_uri().unwrap_or_else(|_| "invalid uri".into())) + } +} + +impl TryFrom<&protocol::metadata::Album> for SpotifyUri { + type Error = crate::Error; + fn try_from(album: &protocol::metadata::Album) -> Result { + Ok(Self::Album { + id: SpotifyId::from_raw(album.gid())?, + }) + } +} + +impl TryFrom<&protocol::metadata::Artist> for SpotifyUri { + type Error = crate::Error; + fn try_from(artist: &protocol::metadata::Artist) -> Result { + Ok(Self::Artist { + id: SpotifyId::from_raw(artist.gid())?, + }) + } +} + +impl TryFrom<&protocol::metadata::Episode> for SpotifyUri { + type Error = crate::Error; + fn try_from(episode: &protocol::metadata::Episode) -> Result { + Ok(Self::Episode { + id: SpotifyId::from_raw(episode.gid())?, + }) + } +} + +impl TryFrom<&protocol::metadata::Track> for SpotifyUri { + type Error = crate::Error; + fn try_from(track: &protocol::metadata::Track) -> Result { + Ok(Self::Track { + id: SpotifyId::from_raw(track.gid())?, + }) + } +} + +impl TryFrom<&protocol::metadata::Show> for SpotifyUri { + type Error = crate::Error; + fn try_from(show: &protocol::metadata::Show) -> Result { + Ok(Self::Show { + id: SpotifyId::from_raw(show.gid())?, + }) + } +} + +impl TryFrom<&protocol::metadata::ArtistWithRole> for SpotifyUri { + type Error = crate::Error; + fn try_from(artist: &protocol::metadata::ArtistWithRole) -> Result { + Ok(Self::Artist { + id: SpotifyId::from_raw(artist.artist_gid())?, + }) + } +} + +impl TryFrom<&protocol::playlist4_external::Item> for SpotifyUri { + type Error = crate::Error; + fn try_from(item: &protocol::playlist4_external::Item) -> Result { + Self::from_uri(item.uri()) + } +} + +// Note that this is the unique revision of an item's metadata on a playlist, +// not the ID of that item or playlist. +impl TryFrom<&protocol::playlist4_external::MetaItem> for SpotifyUri { + type Error = crate::Error; + fn try_from(item: &protocol::playlist4_external::MetaItem) -> Result { + Ok(Self::Unknown { + kind: "MetaItem".into(), + id: SpotifyId::try_from(item.revision())?.to_base62()?, + }) + } +} + +// Note that this is the unique revision of a playlist, not the ID of that playlist. +impl TryFrom<&protocol::playlist4_external::SelectedListContent> for SpotifyUri { + type Error = crate::Error; + fn try_from( + playlist: &protocol::playlist4_external::SelectedListContent, + ) -> Result { + Ok(Self::Unknown { + kind: "SelectedListContent".into(), + id: SpotifyId::try_from(playlist.revision())?.to_base62()?, + }) + } +} + +// TODO: check meaning and format of this field in the wild. This might be a FileId, +// which is why we now don't create a separate `Playlist` enum value yet and choose +// to discard any item type. +impl TryFrom<&protocol::playlist_annotate3::TranscodedPicture> for SpotifyUri { + type Error = crate::Error; + fn try_from( + picture: &protocol::playlist_annotate3::TranscodedPicture, + ) -> Result { + Ok(Self::Unknown { + kind: "TranscodedPicture".into(), + id: picture.uri().to_owned(), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct ConversionCase { + parsed: SpotifyUri, + uri: &'static str, + base62: &'static str, + } + + static CONV_VALID: [ConversionCase; 4] = [ + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { + id: 238762092608182713602505436543891614649, + }, + }, + uri: "spotify:track:5sWHDYs0csV6RS48xBl0tH", + base62: "5sWHDYs0csV6RS48xBl0tH", + }, + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { + id: 204841891221366092811751085145916697048, + }, + }, + uri: "spotify:track:4GNcXTGWmnZ3ySrqvol3o4", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + }, + ConversionCase { + parsed: SpotifyUri::Episode { + id: SpotifyId { + id: 204841891221366092811751085145916697048, + }, + }, + uri: "spotify:episode:4GNcXTGWmnZ3ySrqvol3o4", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + }, + ConversionCase { + parsed: SpotifyUri::Show { + id: SpotifyId { + id: 204841891221366092811751085145916697048, + }, + }, + uri: "spotify:show:4GNcXTGWmnZ3ySrqvol3o4", + base62: "4GNcXTGWmnZ3ySrqvol3o4", + }, + ]; + + static CONV_INVALID: [ConversionCase; 5] = [ + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + // Invalid ID in the URI. + uri: "spotify:track:5sWHDYs0Bl0tH", + base62: "!!!!!Ys0csV6RS48xBl0tH", + }, + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + // Missing colon between ID and type. + uri: "spotify:arbitrarywhatever5sWHDYs0csV6RS48xBl0tH", + base62: "....................", + }, + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + // Uri too short + uri: "spotify:track:aRS48xBl0tH", + // too long, should return error but not panic overflow + base62: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + }, + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + // Uri too short + uri: "spotify:track:aRS48xBl0tH", + // too short to encode a 128 bits int + base62: "aa", + }, + ConversionCase { + parsed: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + uri: "cleary invalid uri", + // too high of a value, this would need a 132 bits int + base62: "ZZZZZZZZZZZZZZZZZZZZZZ", + }, + ]; + + struct ItemTypeCase { + uri: SpotifyUri, + expected_type: &'static str, + } + + static ITEM_TYPES: [ItemTypeCase; 6] = [ + ItemTypeCase { + uri: SpotifyUri::Album { + id: SpotifyId { id: 0 }, + }, + expected_type: "album", + }, + ItemTypeCase { + uri: SpotifyUri::Artist { + id: SpotifyId { id: 0 }, + }, + expected_type: "artist", + }, + ItemTypeCase { + uri: SpotifyUri::Episode { + id: SpotifyId { id: 0 }, + }, + expected_type: "episode", + }, + ItemTypeCase { + uri: SpotifyUri::Playlist { + user: None, + id: SpotifyId { id: 0 }, + }, + expected_type: "playlist", + }, + ItemTypeCase { + uri: SpotifyUri::Show { + id: SpotifyId { id: 0 }, + }, + expected_type: "show", + }, + ItemTypeCase { + uri: SpotifyUri::Track { + id: SpotifyId { id: 0 }, + }, + expected_type: "track", + }, + ]; + + #[test] + fn to_id() { + for c in &CONV_VALID { + assert_eq!(c.parsed.to_id().unwrap(), c.base62); + } + } + + #[test] + fn item_type() { + for i in &ITEM_TYPES { + assert_eq!(i.uri.item_type(), i.expected_type); + } + + // These need to use methods that can't be used in the static context like to_owned() and + // into(). + + let local_file = SpotifyUri::Local { + artist: "".to_owned(), + album_title: "".to_owned(), + track_title: "".to_owned(), + duration: Default::default(), + }; + + assert_eq!(local_file.item_type(), "local"); + + let unknown = SpotifyUri::Unknown { + kind: "not used".into(), + id: "".to_owned(), + }; + + assert_eq!(unknown.item_type(), "unknown"); + } + + #[test] + fn from_uri() { + for c in &CONV_VALID { + let actual = SpotifyUri::from_uri(c.uri).unwrap(); + + assert_eq!(actual, c.parsed); + } + + for c in &CONV_INVALID { + assert!(SpotifyUri::from_uri(c.uri).is_err()); + } + } + + #[test] + fn from_invalid_type_uri() { + let actual = + SpotifyUri::from_uri("spotify:arbitrarywhatever:5sWHDYs0csV6RS48xBl0tH").unwrap(); + + assert_eq!( + actual, + SpotifyUri::Unknown { + kind: "arbitrarywhatever".into(), + id: "5sWHDYs0csV6RS48xBl0tH".to_owned() + } + ) + } + + #[test] + fn from_local_uri() { + let actual = SpotifyUri::from_uri( + "spotify:local:David+Wise:Donkey+Kong+Country%3A+Tropical+Freeze:Snomads+Island:127", + ) + .unwrap(); + + assert_eq!( + actual, + SpotifyUri::Local { + artist: "David+Wise".to_owned(), + album_title: "Donkey+Kong+Country%3A+Tropical+Freeze".to_owned(), + track_title: "Snomads+Island".to_owned(), + duration: Duration::from_secs(127), + } + ); + } + + #[test] + fn from_local_uri_missing_fields() { + let actual = SpotifyUri::from_uri("spotify:local:::Snomads+Island:127").unwrap(); + + assert_eq!( + actual, + SpotifyUri::Local { + artist: "".to_owned(), + album_title: "".to_owned(), + track_title: "Snomads+Island".to_owned(), + duration: Duration::from_secs(127), + } + ); + } + + #[test] + fn from_named_uri() { + let actual = + SpotifyUri::from_uri("spotify:user:spotify:playlist:37i9dQZF1DWSw8liJZcPOI").unwrap(); + + let SpotifyUri::Playlist { ref user, id } = actual else { + panic!("wrong id type"); + }; + + assert_eq!(*user, Some("spotify".to_owned())); + assert_eq!( + id, + SpotifyId { + id: 136159921382084734723401526672209703396 + }, + ); + } + + #[test] + fn to_uri() { + for c in &CONV_VALID { + assert_eq!(c.parsed.to_uri().unwrap(), c.uri); + } + } + + #[test] + fn to_named_uri() { + let string = "spotify:user:spotify:playlist:37i9dQZF1DWSw8liJZcPOI"; + + let actual = + SpotifyUri::from_uri("spotify:user:spotify:playlist:37i9dQZF1DWSw8liJZcPOI").unwrap(); + + assert_eq!(actual.to_uri().unwrap(), string); + } +} diff --git a/native/vendor/librespot-core/src/token.rs b/native/vendor/librespot-core/src/token.rs new file mode 100644 index 00000000..913edf48 --- /dev/null +++ b/native/vendor/librespot-core/src/token.rs @@ -0,0 +1,143 @@ +// Ported from librespot-java. Relicensed under MIT with permission. + +// Known scopes: +// ugc-image-upload, playlist-read-collaborative, playlist-modify-private, +// playlist-modify-public, playlist-read-private, user-read-playback-position, +// user-read-recently-played, user-top-read, user-modify-playback-state, +// user-read-currently-playing, user-read-playback-state, user-read-private, user-read-email, +// user-library-modify, user-library-read, user-follow-modify, user-follow-read, streaming, +// app-remote-control + +use std::time::{Duration, SystemTime}; + +use serde::Deserialize; +use thiserror::Error; + +use crate::Error; + +component! { + TokenProvider : TokenProviderInner { + tokens: Vec = vec![], + } +} + +#[derive(Debug, Error)] +pub enum TokenError { + #[error("no tokens available")] + Empty, +} + +impl From for Error { + fn from(err: TokenError) -> Self { + Error::unavailable(err) + } +} + +#[derive(Clone, Debug)] +pub struct Token { + pub access_token: String, + pub expires_in: Duration, + pub token_type: String, + pub scopes: Vec, + pub timestamp: SystemTime, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct TokenData { + access_token: String, + expires_in: u64, + token_type: String, + scope: Vec, +} + +impl TokenProvider { + fn find_token(&self, scopes: Vec<&str>) -> Option { + self.lock(|inner| { + (0..inner.tokens.len()).find(|&i| inner.tokens[i].in_scopes(scopes.clone())) + }) + } + + // Not all combinations of scopes and client ID are allowed. + // Depending on the client ID currently used, the function may return an error for specific scopes. + // In this case get_token_with_client_id() can be used, where an appropriate client ID can be provided. + // scopes must be comma-separated + pub async fn get_token(&self, scopes: &str) -> Result { + let client_id = self.session().client_id(); + self.get_token_with_client_id(scopes, &client_id).await + } + + pub async fn get_token_with_client_id( + &self, + scopes: &str, + client_id: &str, + ) -> Result { + if client_id.is_empty() { + return Err(Error::invalid_argument("Client ID cannot be empty")); + } + + if let Some(index) = self.find_token(scopes.split(',').collect()) { + let cached_token = self.lock(|inner| inner.tokens[index].clone()); + if cached_token.is_expired() { + self.lock(|inner| inner.tokens.remove(index)); + } else { + return Ok(cached_token); + } + } + + trace!( + "Requested token in scopes {scopes:?} unavailable or expired, requesting new token." + ); + + let query_uri = format!( + "hm://keymaster/token/authenticated?scope={}&client_id={}&device_id={}", + scopes, + client_id, + self.session().device_id(), + ); + let request = self.session().mercury().get(query_uri)?; + let response = request.await?; + let data = response.payload.first().ok_or(TokenError::Empty)?.to_vec(); + let token = Token::from_json(String::from_utf8(data)?)?; + trace!("Got token: {token:#?}"); + self.lock(|inner| inner.tokens.push(token.clone())); + Ok(token) + } +} + +impl Token { + const EXPIRY_THRESHOLD: Duration = Duration::from_secs(10); + + pub fn from_json(body: String) -> Result { + let data: TokenData = serde_json::from_slice(body.as_ref())?; + Ok(Self { + access_token: data.access_token, + expires_in: Duration::from_secs(data.expires_in), + token_type: data.token_type, + scopes: data.scope, + timestamp: SystemTime::now(), + }) + } + + pub fn is_expired(&self) -> bool { + self.timestamp + self.expires_in.saturating_sub(Self::EXPIRY_THRESHOLD) < SystemTime::now() + } + + pub fn in_scope(&self, scope: &str) -> bool { + for s in &self.scopes { + if *s == scope { + return true; + } + } + false + } + + pub fn in_scopes(&self, scopes: Vec<&str>) -> bool { + for s in scopes { + if !self.in_scope(s) { + return false; + } + } + true + } +} diff --git a/native/vendor/librespot-core/src/util.rs b/native/vendor/librespot-core/src/util.rs new file mode 100644 index 00000000..04a33668 --- /dev/null +++ b/native/vendor/librespot-core/src/util.rs @@ -0,0 +1,175 @@ +use crate::Error; +use byteorder::{BigEndian, ByteOrder}; +use futures_core::ready; +use futures_util::{FutureExt, Sink, SinkExt, future}; +use hmac::digest::Digest; +use sha1::Sha1; +use std::time::{Duration, Instant}; +use std::{ + future::Future, + mem, + pin::Pin, + task::{Context, Poll}, +}; +use tokio::{task::JoinHandle, time::timeout}; + +/// Returns a future that will flush the sink, even if flushing is temporarily completed. +/// Finishes only if the sink throws an error. +pub(crate) fn keep_flushing<'a, T, S: Sink + Unpin + 'a>( + mut s: S, +) -> impl Future + 'a { + future::poll_fn(move |cx| match s.poll_flush_unpin(cx) { + Poll::Ready(Err(e)) => Poll::Ready(e), + _ => Poll::Pending, + }) +} + +pub struct CancelOnDrop(pub JoinHandle); + +impl Future for CancelOnDrop { + type Output = as Future>::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.0.poll_unpin(cx) + } +} + +impl Drop for CancelOnDrop { + fn drop(&mut self) { + self.0.abort(); + } +} + +pub struct TimeoutOnDrop { + handle: Option>, + timeout: tokio::time::Duration, +} + +impl TimeoutOnDrop { + pub fn new(handle: JoinHandle, timeout: tokio::time::Duration) -> Self { + Self { + handle: Some(handle), + timeout, + } + } + + pub fn take(&mut self) -> Option> { + self.handle.take() + } +} + +impl Future for TimeoutOnDrop { + type Output = as Future>::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let r = ready!( + self.handle + .as_mut() + .expect("Polled after ready") + .poll_unpin(cx) + ); + self.handle = None; + Poll::Ready(r) + } +} + +impl Drop for TimeoutOnDrop { + fn drop(&mut self) { + let mut handle = if let Some(handle) = self.handle.take() { + handle + } else { + return; + }; + + if (&mut handle).now_or_never().is_some() { + // Already finished + return; + } + + match tokio::runtime::Handle::try_current() { + Ok(h) => { + h.spawn(timeout(self.timeout, CancelOnDrop(handle))); + } + Err(_) => { + // Not in tokio context, can't spawn + handle.abort(); + } + } + } +} + +pub trait Seq { + fn next(&self) -> Self; +} + +macro_rules! impl_seq { + ($($ty:ty)*) => { $( + impl Seq for $ty { + fn next(&self) -> Self { (*self).wrapping_add(1) } + } + )* } +} + +impl_seq!(u8 u16 u32 u64 usize); + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct SeqGenerator(T); + +impl SeqGenerator { + pub fn new(value: T) -> Self { + SeqGenerator(value) + } + + pub fn get(&mut self) -> T { + let value = self.0.next(); + mem::replace(&mut self.0, value) + } +} + +pub fn solve_hash_cash( + ctx: &[u8], + prefix: &[u8], + length: i32, + dst: &mut [u8], +) -> Result { + // after a certain number of seconds, the challenge expires + const TIMEOUT: u64 = 5; // seconds + let now = Instant::now(); + + let md = Sha1::digest(ctx); + + let mut counter: i64 = 0; + let target: i64 = BigEndian::read_i64(&md[12..20]); + + let suffix = loop { + if now.elapsed().as_secs() >= TIMEOUT { + return Err(Error::deadline_exceeded(format!( + "{TIMEOUT} seconds expired" + ))); + } + + let suffix = [(target + counter).to_be_bytes(), counter.to_be_bytes()].concat(); + + let mut hasher = Sha1::new(); + hasher.update(prefix); + hasher.update(&suffix); + let md = hasher.finalize(); + + if BigEndian::read_i64(&md[12..20]).trailing_zeros() >= (length as u32) { + break suffix; + } + + counter += 1; + }; + + dst.copy_from_slice(&suffix); + + Ok(now.elapsed()) +} + +pub fn get_next_query_separator(url: &str) -> &'static str { + match url.find('?') { + Some(_) => "&", + None => "?", + } +} diff --git a/native/vendor/librespot-core/src/version.rs b/native/vendor/librespot-core/src/version.rs new file mode 100644 index 00000000..0e48a47a --- /dev/null +++ b/native/vendor/librespot-core/src/version.rs @@ -0,0 +1,53 @@ +/// Version string of the form "librespot-\" +pub const VERSION_STRING: &str = concat!("librespot-", env!("VERGEN_GIT_SHA")); + +/// Generate a timestamp string representing the build date (UTC). +pub const BUILD_DATE: &str = env!("VERGEN_BUILD_DATE"); + +/// Short sha of the latest git commit. +pub const SHA_SHORT: &str = env!("VERGEN_GIT_SHA"); + +/// Date of the latest git commit. +pub const COMMIT_DATE: &str = env!("VERGEN_GIT_COMMIT_DATE"); + +/// Librespot crate version. +pub const SEMVER: &str = env!("CARGO_PKG_VERSION"); + +/// A random build id. +pub const BUILD_ID: &str = env!("LIBRESPOT_BUILD_ID"); + +/// The protocol version of the Spotify desktop client. +pub const SPOTIFY_VERSION: u64 = 124200290; + +/// The semantic version of the Spotify desktop client. +pub const SPOTIFY_SEMANTIC_VERSION: &str = "1.2.52.442"; + +/// `property_set_id` related to desktop version 1.2.52.442 +pub const SPOTIFY_PROPERTY_SET_ID: &str = "b4c7e4b5835079ed94391b2e65fca0fdba65eb50"; + +/// The protocol version of the Spotify mobile app. +pub const SPOTIFY_MOBILE_VERSION: &str = "8.9.82.620"; + +/// `property_set_id` related to mobile version 8.9.82.620 +pub const SPOTIFY_MOBILE_PROPERTY_SET_ID: &str = + "5ec87c2cc32e7c509703582cfaaa3c7ad253129d5701127c1f5eab5c9531736c"; + +/// The general spirc version +pub const SPOTIFY_SPIRC_VERSION: &str = "3.2.6"; + +/// The user agent to fall back to, if one could not be determined dynamically. +pub const FALLBACK_USER_AGENT: &str = "Spotify/124200290 Linux/0 (librespot)"; + +pub fn spotify_version() -> String { + match crate::config::OS { + "android" | "ios" => SPOTIFY_MOBILE_VERSION.to_owned(), + _ => SPOTIFY_VERSION.to_string(), + } +} + +pub fn spotify_semantic_version() -> String { + match crate::config::OS { + "android" | "ios" => SPOTIFY_MOBILE_VERSION.to_owned(), + _ => SPOTIFY_SEMANTIC_VERSION.to_string(), + } +} diff --git a/native/vendor/librespot-core/tests/connect.rs b/native/vendor/librespot-core/tests/connect.rs new file mode 100644 index 00000000..91679f91 --- /dev/null +++ b/native/vendor/librespot-core/tests/connect.rs @@ -0,0 +1,21 @@ +use std::time::Duration; + +use tokio::time::timeout; + +use librespot_core::{authentication::Credentials, config::SessionConfig, session::Session}; + +#[tokio::test] +async fn test_connection() { + timeout(Duration::from_secs(30), async { + let result = Session::new(SessionConfig::default(), None) + .connect(Credentials::with_password("test", "test"), false) + .await; + + match result { + Ok(_) => panic!("Authentication succeeded despite of bad credentials."), + Err(e) => assert!(!e.to_string().is_empty()), // there should be some error message + } + }) + .await + .unwrap(); +} From f2fa48bfea53ea712cfdd31f43840b03d82edb0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Rottsch=C3=A4fer?= Date: Mon, 9 Nov 2026 11:40:14 +0100 Subject: [PATCH 20/46] feat(sidetracks): finish playback, library write-back and UI polish --- app/screenshots/sidetracks_home.png | Bin 53941 -> 51684 bytes app/screenshots/sidetracks_now_playing.png | Bin 47123 -> 51838 bytes app/screenshots/sidetracks_settings.png | Bin 34654 -> 30754 bytes app/src/main/AndroidManifest.xml | 5 + .../sidekit/apps/spotify/SpotifyComponents.kt | 41 +- .../sidekit/apps/spotify/SpotifyGraph.kt | 28 +- .../apps/spotify/SpotifyLibraryScreens.kt | 123 +++--- .../sidekit/apps/spotify/SpotifyModels.kt | 8 +- .../apps/spotify/SpotifyNowPlayingScreens.kt | 35 +- .../apps/spotify/SpotifySearchScreen.kt | 20 +- .../apps/spotify/SpotifySettingsScreens.kt | 28 +- .../sidekit/apps/spotify/SpotifyViewModel.kt | 362 ++++++++++++++---- .../apps/spotify/bridge/NativeBridge.kt | 21 +- .../apps/spotify/bridge/SpotifyNative.kt | 8 +- .../apps/spotify/data/NativeSpotifyCatalog.kt | 55 ++- .../apps/spotify/data/NativeSpotifyGateway.kt | 39 +- .../apps/spotify/data/SpotifyCatalog.kt | 13 + .../apps/spotify/data/SpotifyGateway.kt | 1 - .../spotify/data/SpotifyQueueRepository.kt | 6 + .../apps/spotify/data/SpotifySession.kt | 55 ++- .../apps/spotify/data/SpotifySettings.kt | 44 +++ .../apps/spotify/playback/SpotifyPlayback.kt | 244 +++++++++++- .../playback/SpotifyPlaybackService.kt | 165 ++++++++ .../apps/spotify/NativeSpotifyCatalogTest.kt | 117 ++++++ .../apps/spotify/NativeSpotifyGatewayTest.kt | 55 ++- .../apps/spotify/SidetracksScreenshotTest.kt | 10 +- .../apps/spotify/SpotifyPlaybackTest.kt | 1 - .../spotify/SpotifyQueueRepositoryTest.kt | 10 + native/sidekit-spotify/src/catalog.rs | 203 +++++++++- native/sidekit-spotify/src/engine.rs | 16 +- native/sidekit-spotify/src/lib.rs | 93 ++++- .../sidekit/design/components/SwipeReveal.kt | 51 ++- .../sidekit/design/icons/SidekitIcon.kt | 1 + .../res/drawable/sk_ic_bookmark_border.xml | 7 + 34 files changed, 1602 insertions(+), 263 deletions(-) create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/data/SpotifySettings.kt create mode 100644 app/src/main/java/hrln/interactive/sidekit/apps/spotify/playback/SpotifyPlaybackService.kt create mode 100644 app/src/test/java/hrln/interactive/sidekit/apps/spotify/NativeSpotifyCatalogTest.kt create mode 100644 sidekit/src/main/res/drawable/sk_ic_bookmark_border.xml diff --git a/app/screenshots/sidetracks_home.png b/app/screenshots/sidetracks_home.png index bdeea4ab7952c3453637e715534664648f1d3408..b7169dc96f037ae8543f82c07c75aad1e267b800 100644 GIT binary patch delta 25778 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 NRY_g3Sl%M={{V9$A4mWI delta 28053 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 E1IDw@5dZ)H diff --git a/app/screenshots/sidetracks_now_playing.png b/app/screenshots/sidetracks_now_playing.png index 267b29b801aa9dee50c20d6b383d5e286429dca1..564ef2b415cbeda751dcbe8ae6208d1f8128d961 100644 GIT binary patch literal 51838 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%0D85rGx-iVpkqY z!{>p+AoZqxbqbkEhNx?5j`5UC)(+2SP`GV(GPklAV&o;)$Ue0&dmx{<7J`1u zxp#e`@k}|m-t-b2nT2N{S%){G-ho2cw+n5XQgwM7a0&KJb@vrNh3Tfp8EEm7X6@jpRW*5Db~`Cm%&_yF z_i4ql$;H;=PFbF>t^4^4UuR6s~3;eBI&U=GRH(KvtuD;gOMca}tq zYr=d?4@#3S7%Q_oWK&*9SiWzOFe+-bfL-y3B&=%ZBS#uM2mvMRKd(>rKnlo;cBX3M;SVdxkUP zFpM*ke^8RRU@5VXnVejVhV;hhZWO5=i_~~T-8mosUTS#3`H2KfBVp8bUgm4?clvLt zbP*MPkG+2Vww(bE^!iE@3*p{zB*w}(YsKYgm^8YAwQ1_Jr}W_qam)Ji8yjCH-A^JpLC7)hB{fK|U?v#F$;3!| z^vt1`Y*kr_bA)_w2+s*Ua!X7`M|1b-oR!AGKC!MpO~mfJf_u9asa8OG^%9Bg3~n5S z;ueP-@4jfC38l(F@@PQ74V9`!Zna(wt+1Ya*%l&K@y{hx>{t7F3VGlc;0f`;cWPg& zINdnWG5BO~wVkGxCD`1E1pKHY?@RKzi|e#M%>Cb<OzQ!L=7?-EbGx5e6a>Y~y+ zn8JpN@hxFdhd!I9fO650==E&Zv*JCyw|_EP{jZ_b+K@H^LrzBG*kpWaIi4cFNT227 zT_aSjSLJ}-@j1nhIE=3F?`+jqK%tlWB`hgh*j;r_8}jUK^%@f#;h|VKf{9ntF7may zMHY0XQ#R5GUqq^hxhrlmUrdjRX7ZD+qzOHJ9K4d)sMr^z2u8`pTob;0LM9cD;Y3Jc z6ATm#G8c3o#JJv7=D^~%s`BQ{&?;8 zC^6mSIoSV=)k4E`w0iicA|_e72rSP#I8)~|Y}vTQq9YC;i(g$2Eg>2!gEndO29rk? zfC*OP?cD7XNN3#S1tgNXJxdB*9r%MA4^?w&y&#?{r9!<{+gbA021$~vgs$H$5Sk%# zlt=AdM92qnCfK(Ci3s1Ki5RrZjc|XQ8k!BtE=}Mmo1l zqgEamAM^W|B)m-PFgf8Jd9mE*U844HF^0o&>UD9@DOaT}UTlQ?@-#YC(3>+Aj);c0M_)eu_fFW&&RdWz1`=6j>xBH+eh z%W`|pp%X`i6V?ll&f-WUHHR5I-^?tWc&dn?$ys4Kbnao%c?Ye)rqOaC&oP+GU7_JwKp{t5{nLoLuMRzG5CXYLRkiDphGT zoqH|%8AEX7;7zU6C{dfVO<@i$6Pqy@YuOZ6x_%C1T+l;IAFM(U!3@{@ParmoFhw)~Hk(Hl238?hcWAPSyFOaejsP5;& z)vaklXA7}|Ceg)Zo1I2oj`?m|=`~!Sa-H-0#Xdw~UF95k*i#)d z^8C}c^|^hgJ^Fo_F=M$*0+)XPqF4R53FVvEG1OaOkFP-a@wPY1;e}sT-iEngwOjFn z#`y;!CcT_#-Y^+=UzJKUC6g^0YVlQ7-f6;P?}@=${+R)TT)%2#ZXu%@V{h8GK77zO z@(v}syucDYj=#kgp3@3v)jbrN1bJ~y@T^?*OjSB17} z?VX_8Ay;kUhkU%E4Fa_iaqf;lj$l1+iLORthiKGdzImp(jZ6NjhhL!Ds~chkUhKZZ z(4)%ynJmv6Z-TXpYtkG21MK1iv4vZwniX4$K~J9ylCjM{L8*LiODfI0+UUG4H!1hp znH<4%Y4bmdD4V%0(wPZ+_WFl~Y|ONWb60sEtw}*n7BL)j1{oibrpHF!mv|C5G~gNHQ-%S}#GbdtQ{M zfx!0-QSX3hvL7Bs$<;OFxTPd5l7AZ9LO+KsrIF^WG(kwYLISDhO*--QPis8YgtV_? z0-kQO4xNZ;5~bx`EP|WlfVxX9^dMI9D3rZ_(pGvv%shy!NSfAz^@gkG!V@VPdEnSw z9xNHNP^@XrI`yT>dTFmi(tR`J9rv~Ux23#Wno~0?HgZ16G*{|<+Pkk~A#BFm_gQ`K z+}bt55oneLV!*?F`{qK*UX+hh&p<=`Wrc#oTHG+LdaKu7I_~~T!Hkkq6RN&3Nr5pg zlgxUFuYK_Oe)@Yp9KvB|aG3`a%+nBw%0xgK`h#M*60F-5c56x1X%QtLXUbN`!7)Z` z`bTYR>*2)jVWT6MjwjL4!*2ux3Fzd)nlboMl}TFmerNFbgk+MFN%&!DaFsMys8FPE z^ZgpHA!}D+%z=mB@*w41Cc-O_60J>PMdCAQ@@9gd!beZk3Bjk$= znE$xj6Tj}U@Yg%KqK3&J2P6RBTySqchHdz^BNFTb)l`CZ1Nl=tCwxwWH)E?e27t} zWoFX#2k0BmQg;ryN4qY5YLjiUO`l^q=_Y7!$7%cPIfT_Hz0c~N5ca%qS1j__ zvg%Rom?&vO)c+&o;?-HsD_HaHbIwE9b)K?FB085tvlt&DbL@wq^`c=OrYB9|P;8}i z@jH#sE^w1x!GU(wp9s zAVO#h@&Zya!z#jI{!B%O+Hw6ss7+Yshs%lmV#J_a2D8wap9|)ZGE}lI$!6=WKCD(5 zItl%t@-M|s7ST0BT#5qm{V4#2uIBRVWu20|Qaz&a@30d$ad)|!HhoxJ)s@&1RE~#p zvqRVuSNb$UL^5!Xvj&Zv>qJc8`j~~oCNsra@`dBgmreH*Dnh~OmvDZWcJS%19UVqM zkJ7Wv0>pu$tp9ljl8T}}hkaSeSv6Z|C&3abEFF4Pw1_PeuiE#5Jh)Nbzx&j>dXRhE zSk6ee@PL-ZuxO`xGxgQ#C8d7Y+U^1ma|I`}3rPA*qI)DEnw@qhV&y}gkMGxEQdw+> z&S>_;Em0^KMCUe+Uf1%H44!{#wou z^Y{}zgtfLLn-8f{!oPpqyv#qfF6<)bQySJOb|S5H=2PN2-u@6{ZO|Eq7#FdMEGwVq zK7VxbmyZlg4j&q6?q+;KoFJqx5XnQUn8iiBkaeGTwj@Kvy1CP=fhg3fMvJa-=q;QS zN6DPX@V*kkj<%Jr{Z1k5BlVoorU3N8b(r-=d8UWb>f3^`h8_W&t1`UzIO2aMjZO?0 zZSd`|yh+VF{l3%W#E@*VUNet(5x%P#zHkm=hLIGp@3S9Y5p<=7DCup~SvFj8JDqgD zs*Jd7C_2iD`^*la&hkj)j~!8va-SL$Z;lhU5)rr>3UbGT?0WWY-)Y>|lU=_f0VU64 z!hZhglPjyLtkv!#L+u0uX@$Ky-hBHZ@6T|nEWY zZ_yIL!Rf)|@GqWjM{hR1y8o(u@7Rh*%JpO#%M=+YXVn?`gp$=RKH8+2o#5gsDFBmW z`Vj9Gu1bk*USVfuz1R1;blK4jn>1R!dnqIIlCg#yCBVA7jP$wn^?0qfJY&=h0hG$q zwi>k_vvVa{Q?&Q^)eSL33mk$hg8m09G2ZxBIH1skY0>i8zZDZ*MiWZB0z%ph@7(=6 ze&jSaWaSt@nv^LS4a?T3DZi$CRzQQ`u06wAb}>0Setm<-t|@V?Cc?@XgE0el3njhy zt{QGnB0UDi;?OM(+17ok^aPVud+C%tSUbac2=*S{hY2-FR5A&pr7)cKb@5W#;u zFYeroHyXx5vWEgbz5=!n`DIfsbIQ|{?8-`ZQ`(&)Mjk&%)^t`RzN+C>d>>*VIbX7H zcGe_fCS9QORwJta5HNgId8Vgc(tI!F;k#&IQ^(Sg_1(~Sj@H?envt_QD;`n6++pNF z@Ep~B`-+gG9ems&Uc9!ZGe*b?C_U8vlzoCbUFVmzEBCC93!q8Es$=gUoitjV11RiDoXkP;c5Q0V_<3QQQtm*@?Pz^$ zQYUQX3Ohcg%z+E$36P-@80Kw&PlGYm`9D)QjU+E&Pfi!RuSm58qD;ok+)G98M@GwMh;s_fcq?`F8(N~{O5`r>{8MbYG>Gng_d+FyAjak_at=jn?cBCHua@qg|btvOH+E8t?!|a)AOv zEB}(~*#P27H30JIN8wSP*q%G_ky68_=bl^f;$xm`rw7o8IcNW9djgHLmx4`ldM(dNcmh}|M;=V!2r}7%s zZr3QP|5yjL4pZXknDsg9poARnhEd&h<0?N=L9SR6`ueYvumMVYml`_An0HXWgk+H7 z@e%tXgP=~ZhyH^I7%VLX4|8}1$tDcP+vRG-%VLXESkcR)lrYa03bi$_5hiPXZXMh~ zxH{%FBLBI&HWlAQcN7FD-Tr3A6^VXEb^n~NqA%x_0)X2ljRhdfPRokS@BR~{;P|mp zf3P-xm-AF1@#LVmmrs{9h1>}}{DAI$d=RakZIUZF658}z0Mh0HN(#kD62zB3ha6)o zQDTLW^|ws})-02k}rvXW~D<4|%X zrV!@jK3KqLf!G4Ws zw7|R*ut_`Ig#F`YiaCHl-;LX@d^p)fF^bd$#7LWYd0&wb#;n#zO#LlsIEeZ#DaV~# zreEjj75^-BHJ-9hlg)O@Ma?VC^YmHl=PZr|v(oD^WQWG%gOBG@rL%l|S(A#S|BIHO z8Lc?d%*B3p@C&>D-kcCl*u(d0_P*z+Dk@U`2bG)8`&2#A2$MblLasDx@yDE+?eFrV zebhkl_dI^GMC5`l8PW+tOC()R41vL#Yu6<0E+|GGlB-hQ!7?C!w=#IY-=0KZE z4|N#-3EB2f$aSkd z^C!if(5P*p0Uxw`X=FON20}{$u*;ZW(FnJ~1JUyeUTl}{^}K)Mh@!ulQ^&XBMvi21 zAQ99W@>+W2OVCip=8jC!+q@rEQP{`q#fMZZat7_7RxLSH!Ybq{Z2Nzj@$7paf_Coe z2}gcK{LwA9<{(je9|^|tRkX~y_V+Ncei<~wH6X{ySJ6$?L*L=1ZM;YbE#{_G`nj#2 z6cdm4m))Nd!W&lyNa5qt7JUqxf#iT5eD2r&Ou5sGW#%6kBEqQe8spid1Sze>88ev# zlMgz_uSwf;4eJG}zv&tBkQ`uJl{EUvSmriJ0h`?<%hEP4wcRb#MX!ZV3)|}Ztf&44ztY6Th z6A=nqnISpy_`-8B8!pogjSO=d&>##V0yL|m)}*dp=m;9eaOx9V%Ps_`TF?sK=1if` zJjWasnyk!6(^PE-#OFy(r)*E?SI8HA{Ext>-gtR`4+su``{BodHCWWh05f6tr~ zkcm8rHWECblbVsosKwqXhzyjKNMp#|b&R1#2I%SFz~H43rD^`h0-Cuc{0pM>y>MK*3~?+VbOs3-d`cMCSK=hjJq|c?lBr?oQ?qT%^l!*nI|~H`7F%V zHREP%PE7L+utYpll>fhp6w`CqLqO2?Yc?bWrV9B1B!>6&?E|9>#CMk#PiXGf=Af$J z#jqS>`SG>}mJA-jwFXVq8Nt=-k7xi{BYK$D7dMVcj+zf`P;rm5H5o>HfGv!M_sD=E z;u)Z)3RVjO>P`oQX^E#Gd#?UwD(=m!=#8%Ks5k)4*}ye^TAR-y1zdpAqu*%|)RJ=H zYOa#X>`v)vzAE}Tb)5aB_|Ki$e#VMKAZhkBVJD~J?Drxoj2kvVePdX`Ed<}A`3zr? zKQz{R=vvnK7Sih0pw13U`|q8w5gi%G-^Mb)_AtHNJws)-F7nn3{|zn%fFEcKSULnG z{MvZ@ga8hjYy&u7ag$uWV<-{rkn1m-iq;H0&FOKTIgPNK)gP@^T0}I`?2%;>@xy7I z2llDXtr~FC=K_?e{n#>kkEk~9BWCKPmedx`rG+!mHi0ym^V!~CRwiY)4&Oa?5qwb-MC$rFenAB`Q8O^Q8=g!^LGe9Zb!rRHReI3&mv<#`Pg9T!li1~kYI769y153GSt;<8PA~4$`9uFTlkr8(7 zzmm}c+D+*Pc?#6dZl7Rsl@Qsmmt%frqreN>hg7^MBpk`0K1nZl(smNH$B{yg1@;Uv zv|}|Op-z~u_#k`K_UC>lP@ieuMo%nzjODBXKm?CYx~aGVX-*6CfaJRVJAITc8bz!O z$s7L@_t1qgZRt?}RyhHnX=i151L)0}Tp&$?Ew~ZStzeF0$@`H$Q=$zRW%Ha6Epj6m zh*Zk^mrS~qjMcm@fT&*>p$}9}9R$!g68KQk4FP;y`C5qaZ;a6bMu}RWJL_tEvn*o) z)E>@ixC)<}@>tTW#|#q6bKv4K07G&Wxm1)uU8m|^mZ+QyX0*#@AjLh41EU5uGFwRe zi#)TtGc6H61%R^OL-*bjK&dsIf>r8(Q(YZT=?TWHMXJEv=8N~YDFeSIK>#NFlY>Q( zI<%{BE?D1!paQt(tcqfoYbx^}{yABcb@LPtB>AJ>74FF2FQ1IK%Oi3}^6>pk9D&dgEx zz?h&I=yLB<<$9D@yZp&s_8km?KksM;qW=Oy1D5^4wkI5j)5{|Aih$}X zuBLydCDK8@FayX2UNJ-j6T%lMBoycXc0R{3&dU=4Q>O&TX$YVN+{q~GPgSfJ=KN8b zZWB3SY0u9YS}U%7cK&zHf7oHcVg535o3A6w14-a~g!X!V_CWA3lZfgkoUcfh*7@*Z zId#}eB!~D!kr6T;I%|8rU0C6!Wh27kCZCyIvNsx{19M!E`d zj)kv!=;dXv@)4HXs6=)wgPYQ${LyY<%i`3+;(b>SBvM~due69Lq)oe=zG(PQ=+ar| zsVzs?Fq)*LUV7E&5FUHZQ;SF(_?@-yJ-;tM`p@1B z)%;fyhrOQB!)D*faPpuMKtE=em6O&f%O8M21n|<9PkukMW{rZ(RObFv)y=_nh&9mx zu2ZqY2RcZ@rXcI7|55xLN#P%<0+C&+kvgm_TP#MH_Oa@Rj=v`}x`M_s3 zGQ2MnBxpTxlqWRLkS#Sk&1OJRBMq16HygnyK$_0&sE(&ywV5#xRO>B<`XI9rA|U6q z3IrBm-0)YAq+yT_lmEAbNmWMS@EDx zzEz4`AVBtkeGv~vF@DsmShSnxYl0YYq$Hw9grJPDucUI5b~8SQJp*1uy5pGuQbyX? zZPN8W>Fw#Lc`%G$e*x>Z1e1>zJV2T|Y0o$?%dl z4k!NtNgm4W6X{PWC<6i#B>b^+G3>AU7|iE3(S^1)8%&Ctb9`pWpb*&U?KslKgT6kw zh;_$%kESJlxnrKLZ;xqjsDM>&?Zme8Ep@&nZ8sj58)lc6G^Jvz1H8LStVJ0(szTM` zcKcf(kSzrwz0V}gtnsBNw_yB^; zJ1H>%hZUl~5=Fu`TtD(x%K~*fkCrtqtdG#U4_@t)4fVPX6a{7A#}28Iqz`lFTkNZ$ zTBmjz#%5%UKlYvZo?M0cO^{)|xSzG|T16lH8o4#nhi`0m$hT#{?xr^~o+A05iorZF zp*>Yo;)fbPR-`oWn+&=TgF#_(qSD$c^{T1=G7%+mqvIs{zuFJXKzoG9Y|L%G<kCnL+Z-Pweux;zAe#+&mt=FS5vdEW!g~l$#a40GaItZrT496 z4kz!l50(F}Kpv~yj~-U>8Ra&&dV)~I4$`xRw2nZV7BKx|3K4)~Iq_f2oI_%!8rm$K z0wBdkGT|R{h0UVV(6{=?#j5pF{cx4{FUa&s2sWcfIt3>nWnk%V^5dWZ)SVRK6fU$T z(f3~>EEj}S*iQ(FFkwbZ4$T+nrw*?aGs5&2OtdcpC-G5G>o0jV{P$ShW^cCIx}G(l zM4p3?ZJXf#l#%R@63|3`A?BqsQ7M$^qs89qJ1grS?`~I)(WtP(9+==cT3O5#c%8Sm z38NH~VOxjEgm4oxjE}Y9Nv_?=_lEl~$l=C1{x;N%@R||7cqOsNJoS|TR4oUv+wyK8c}OfRJvIV#^`b#E$j z`-{9v#A?&HhZ9@Gg~HYXb}A!`>j8~IRK4L=YU;RA&0j}gSl*w>fe9AE{9n+r6K35t zt4E;|PwIaa*c)4(s}a-n1L4*7L31}NeCGhOgfh6=v(j2RPuet{ce&#cL#Rz8@5!bv3V~E|CfB|I|Rh+ zajo!HnPHdDm-eqnL5O!;00n=<>@e*;n`XS7y!lyHYAU2v6F1&%+eApZb(*KcLegpSo4?E>JEA82m3z()TuhD@t?LXfK58)HQd*+aj2Xc&) z(gkSPxklBRS&54n6W+|6eq#nd#Xiw(s6bU0pP4gH*Uqteo`Q}$kM)qMm5ED5yXli1 zNCs``?!Vda4cvA+K6ENtjAns3HvAjX-#o+BCs410_bAo2o-|IBnyc)b|bVT@5_>ub!I$tt)ee1T?+=Fd=|cUPqPWz;d=fJ$v&Jd&xc8$XsDI5V`JH z`sdbjul@Y5@bsGFZZK`l8DPKCkUjbUpJ9ySA*7UGnum4#b2Ol8OWMhe4NKX~ZOh_8 zu$yorB@~66A2+YYYe?EZGR+}f|66bHL@DK$>dkYK^Qw(HahRpP_FEbVps^1g0s%|d zvN!hRA@=xJNDK^ZoN=cv=ugcB`&=0psy4>xdNP}bJY$e1%|{GBaowUeFwUM6wLrEB z?ZjapD|~E9{7YXtiJ;Dlix}Pb^(dX;zXb>f02}y2Pmm$_KSrk5Jl+SR);kU9hvgS$ zMenvMD9A7JMUT9Za(+)Z5}j6~(#DdZBLNrDbQS^XQ$%E#`I8Fpz&}LN!;I(vTK+W; z`DNu#iJ)_MMvA@v0I%R*&w`hE`@3l>ms*UB8Ixs7#PxTd)gE}CDpAXtSumt%9SR1A zNq^1&)3`ZX#@Cctm*1>X7PY22Z-DkKI3pvZRb3*PWd);=Md+R8`u6B3cm4ak`j{_j z1Oq{D$7HuvYxyp$pU;CZ%STn%4x8sZHW= zcv`ohf&A7a{>Fu+Ysq(H_idm+;E-;YW7+qmXt1~RF4O9j_`=P0vGvu1V&CRdj_=5} zg@%7~?G%)uVsOMI;ry)@0(PJH2io1J zukF76m6_9aw<(0hu>$k?-9uAti6Ed&>BBi*ZiVk zd=c7oYvc2&^EU_3O*RUWsm12(?QGFmgtj<2p3?}O;?_71SEN<=o;F*=w;O~d-Bb&4 ziHV`cF4L5;4&S5b{bqso?P2gIw~1=ke)f>*e8+!LEsy}-Sfob;{}vAF+oTuC_HJia zw+^IFxkX#R?4Z=Vh*Hf&bsr~!iwr*DSY8jF&UaCOLLdR-Uw{4>jI{uq&};%r4M@p_ zD!}ri0w2j7?Iz6ah7cSj)mG2)V?S?RXy0XA&ixs1`^8Th;rj=QdS&`g!%`pt6a~-| zVP3H)=`1CQ0>FzNQrPefkj|6QdtnvKpTj0Jr#Ky~*8ycGMZs+&GPt%>INO%Rq z$=x+tJhH^=^*XYao&lgg*rJr%GPc(fkR<-n`6OYe^`27TbX$o`Uw$PO8Q;C-xyk=R z3i57kDT`cIyX?_gEjWY>eQMh9zImc#byKju;G1yzw%pTjPxwd(g7QZ_?u33mTRL)GHM7a1zhgQjw`dU9J*y*-O<+z}JF zxUvyAgS$)**qL@BLi>IH9@5~`r#T5`6VCbWkJ9@iHP5k){#y^;8~11#EgKH%Kh|lRzbd`Ic$~f? z0>$s#y0HMfrU=_|BYuMIGHL8{Q32c*pag5lgjf{8sNr}NV{*X)ockwMtRaVO!)r(0 zDL3x~05QMPkq=U`kt{9hJ1RvSO0Nl^bxBXz^Us?O}KidP)HIv`&xj zIpkldtRK#WYk1FTEUq15PZOMt1_{o>x)rCLZj!nL8@+S(4U}6g?$b5a z!-M!-|3~~3{1M+Bts~Rr4+iz$c>I*c%6!9Zt4YGSaEB8AIk9VRw4PKonDtnGJVjlQ zykA6?g+Z<2am#6%t-Stq|3;)GEs8*eewOZD4PW+rN4zBL;p=R zm!Jt~of+0*Hlnq6h-TG>@Rv!6&2Qa!3e**DWJxamSKE^ZJVlK{0Rt)(XC-Oa{kiBZ0`o77%5YlTASk&z_4cT-!`(S z?`ZWNd)&grZeO>Zu>hzq!`SM7y1gVUdhHLOu@fKu5X|=0;`++dZZ@E|FmELN>HL0H5PAiJoM5iOI71IMNFxDYu>h5L` z+qyJ-mFMqY(KMb`oNB#uo=wX_|UCo_^aFuHMZX2 zr28z$T-qyonDf1##X{3a_a#H*8ai2?Z}PqYMP|=O?75VEKij|3{&&27QQNq)bg&8T z$rDc-u8s~~ht+mOvb`lT=-8)g9`aQ%Hd+~{y4W3zt)<}|e(r9(uDri?nN%2gV5X9@8;d2pCIjx9P6<@e-}~`|t?xs;` z<(<-7eACI^i`j~l(Gid`cCeHt)iX}OJ>V0lg2awxmrW`k#G^#|DCBvpa~IRD#P?79 zEj&MU866vfwf`Lcwxg%HDDqZRNsnymK2ejTr2HDtWYz8=H7F`rt9^MvYcnjVTAt(E znpBOeu(pxB4QDR4p-cMwiq5`yzE*O_&%Tcy_EPYt*npLhS$xPpQRCtm=J+W&b~IY`c@#rO~D5_c(B*(2E%8*c(Y&;rUH6!z8e`Xx?{0ug1AMaPDcW^S&BXu_(;_ zS{)Z4>lbh(aI&qrFNY*dj&G2qc(XVE%c$+k`ARbqQBqx^7h<+f3n|l|5pr8NbxF9D z7;X!D;-D$h;mt(^ZZt{;6zY)FdB;G% zCjP&3d{H|SD~IrBiz(U;uwR5u^rnP@_H+Wmyab-nAyM27r2VHO@<6nE!~i3GZP7Db~P|{Qqqm z!yd3LjslN= zq_X|EsmAJdrd`$Pii(Wc@6si1lyK4W!+pgLEqQ@uue67PD{bz`glR%WZ;L{mT68o< zPu-cy;TL@=(QI~m3aiR2;D9vAk{`7Q*DTm&AWG!DEOX%X%4c~m)CoO^l8#sy>nvrRa@%UHC2IhJ8#-#u|kr_AICmcp#<&y7jG>-W83YW zhPYI*(z>BU0zYVLhqCOF+$(d%wO~CWM#-Quj}B^Kb_au?BJlrd+rfssh^2TjDa(ix z>WmiGnO07t-^w>sHVy;{O&x-8WbAatqH+#3-Yp6Ye!NlwcA-2Ew;;Q@#3w$FkLYc- z{C}){WmHw&_BScr-7QiA(%mJ30@7X5-E{=%4wY_@Zs{)R=FrWd`_L)>?Y%zF{rG-) zznwA89&4^W=Uj9BW^U3ufz##1_@Tf8FQ@3BkF4XeBq5nAn=IHFzX*%D!dqL|*I!hd z-?W1rq*EJCt4V=if8sxfffaVOTFCZ{iPmUxUys)@F6@hymWuI>l-CWvfNt5RmvEC&7laIw;@BVOvYh*Eh%q;%xt<u z$s){8Hs9%j(ngCvVW`dMZ!NXkM0my*74TRF>GkBV3bDqE217$cV$@#80t2>)5iEk293xI9j?cOXv#3Lm2~%|u zzBe~E7kjbiVWx4wMLQ})B>Z+Y>0hF<$M{Wm-5~%u;M#J#ZP(HCoLyX7wc@INPczolqnxK{6LBFdpjPFYgpUSY z%much_XRns5AWYwx6^RDw<3~)hJABQ`9Z=ZqUoXw?j>X8i!lXOw24(-TOrxt(cuR~ zp>z(~obyZ+UPsT7K3rPX_V-U964TR#1;z+Y{p_k?^QHw(lYl+Gt7o&%VvC#=LLo`& z*Qp!Y)}|*AlL|f(bF7~sP_J1I-tmaV{x{cIJI83VH7;W1`Y&hf|MC}wqod_Dy*&~^ ziZ-oTznfR@PWqm7{T-t6EY3#6>R=GE+^09;RWuYaw-MVC@EeH0g|+9+UZ${}O?uJZ z%0}%rbkMg`G@4|Bldb;(R#s=3^W_y0pB9aN2M{Cj0eYJL9Neg3LL;Dikm@x?#~|iW zP1xp*R&`cY_f8V7ZTowiNz~G#G?PF%M%&9(eP*IVQm~OFVfRv99pl267kqDLecNn@ zr4?G-GB}^v7h&xCsN1#lwqMKn6w1vU3CncEz|g^IT$1?Dj00$E8S5|F(yNDoAIa)s zl8BO7GEYdyO0)w>xWezgV=?h+e_jt875$u!dqoWH-O*6+EqzhIpgZyLQhAHMU5myp z+SQEE5L!@yDK*|5h82=`D7#yBV;<&D(?0g-Q1RCg$J2JI`4m&|XZyx;5rr6H*JNO) zC*UE{J1(%(#fH65=Hk>#{{gD^O}U7wqV5ihP``5UrsQLsTp++TB&``$6sG16ho%){ni8l;h6joKKStK>X`eFlQ`pip4-B!weZ!RV zfAM4>sA7yMv#%hijg-Ryu%kdkg{UGGZc+ZG>z0Qce+3!kSeL{rp(rq<8VVYLAhej(L z;$bvyT!$?mWAO&BOf zdHUOJP*IxL8z6xJgjgYO5VQzj;TcgY`XR(X13GT^dm?AKw>`nAEruM+3bavj?>#BW zUcVCoiAVmll!)0BX%0-oLT2w=F0y1tuq)LS2(NU(+m13T0^q?YwObP7VjasA;{4SR z#S>h0rT_geQ=40_4f&coni>#rD7gMa-hX%7Sd=-ZdMxe>Au2{LHuZF6bi^gzC{-bQ zIOF>?6Fb)xCT z=in9gL}UjXbK&NS4V8&bC`uaWKJ4m*B5p78bP{DGKv0EUiSN|`f)YWjyb!NPIBpr| z2e5*it9@{Vdcn0;bTqHLuLXnd%zCY#RCrpWnSgyebtltRHJr{KBaaJ`@V(zacWEP2 z;zPV9Y2y267#kpTHR^~!OrdeqD?uyB%ohgQs6Ak;;)=8Fu>C@4>@Oqic=RI~Z_BHd zQWyyDTkO&+(;Q(Jc&?SA+Ov{xU_g>`fxb-%5h7n?hh$j_GoP2a*F4OpN}pGp;DhU7 zc?3$MQll7c|5zCR#t>UQ}4ak2j+E}au$R!@9ZL3$iyJQ+;x zN0ue_v>;>)$39TF@dfB)ox-VzBJ!)2tKBI>FKX;|*s0HV|1)=owd(fqv34G;k|Isy z)Fi$=UmdCC8WD=dNoQH6H~OcGlb$+IM7PJ5l3pTRHJzSuV$}DEk$wmtE?eARe}Bk% z36h74n)^3^>HI0nE%W>pap`M|lMkYM-v~``97PZCF-LHX`%y{`^)(hd=^FZE8jwp5 z?-Vj<_RmJ%uFnBg_zp079|-nH8tkT>sw4>9rG8R^wqab#B%nM*3jon zpz5ihErw{@RnseQ(hVRlpyL;iSEJPpEYZfUJ0oW1d9~XT)U3chN)G2Mam;r+%>cUY zS)~Bm@!*JdrrF+Dl>S&_`ES~^t#ZobJ?SxV5?UWLh5OuK=1{Xn@5-=Aech`KXsirY zYsZ&L%!Wg_w$ky{d1`J8;3gY-=Ww3sXL?**4SJoaujA)u{l4deU9pC;)?;Q#8qP80lzY zD-8Brt8S^fPLA6t!2Li1<}B=+?(Lawy}0nFIHOyNXZTpGre{7;EZ!9CLn2b&%*5|( zf@0NoJcF4S!VQrw(JoAj(jSQC$mgVM1%e6^Eq^pzj!5N5jOF1e4D+8=CS&k-9-_GZSS3j?*6XilXT*5kP- zL(GKjA+`x-ZG8T7&30ZI5o>RrVZ+K_Ft6gT+eEh~=+l+aT|F^*A?GT&V418%55(ds zafRQ)^}icyQmfyEPv=2^lWHaQmfce46V1ZjtiPhz7lGk>flaVZ2=R4{Da1`0E+EiV zSlp#f8i$}?L0ThtafB-mx9>Myvh5OGi}N7Z4lOBpYi1pmt3J|jtk*X$g9rmr!ywD0o>t>Pkj#@weac7Zg|H2uaa{&?$*V0&7WOqb!?J#=xO@&a_J^=AsbIWRhTrdLc*GQhGo~9uKXdi8!<0Zrv#Kmz{;J>Fs)2a8 z=jQ=Lzff3a{n4eRLvAUA3;cTSeNw+y*9*p&GabH{`-eha(xrp(=nJoR6nrhnq2G_W zZ-lndMTXt;xgCZdq=1<)O}D%!!tzUoty?th8__LqTmGlckRfq@Hk?GerZ3TOta4ja z{9?jD`p^)`#3}6TMdXOlcc7sePC69Z_yZTN#pPbC{s|$+NQzG6=Pgq`q_d`#$Pszh zIgdtSlReFftnix5!CnEY9X{syZ&GD>a9OZ(XzN~|CM)2^?{(rUZQ(t+nMt08+>r{| zYsrOadPO%wQ}0Y)ci#MPM*7Pqd4K4UF=_KjyOG;)p{iHK$FbF%QAXz+W9LB~5)3Dm z`AX6-)L!gwCVOfsjL4)(vkGPu>$enScJDTDc|UX^=8)s$Y-%OJx<7V5S5hdxe&r%f zl%-jgg&{pMPu_F)dsKL{oHFI>!4G&{g-z0p1^dZFOq*X^1*B$VU388|yZSuBbW3{Q zF5fZUOAc?~6@R{EX!gsJ>$u7PcZ)SBj)xnjvtGaTn8%SOneJdC7`dAAP(O5G728Wuos_1qHZ5e zeo()}ssyuA{;B`?C)NlAa0X7Q4r&ii6lb08-!YH#l&3HJq)32FJpQ=4hA4247giaD z;;R(X^a@%to`yy*PfA@}@7 z66s5pOb7LdYr_ubPjVdxzc+ZI?Zu2RY&?EAd;G7>qIxq$up*iz_7|=GjX_O_Tj5WO ziYzw6@RFa9`t)-1MD;aGuUCP~M zf#*a=i;B}Rp-sg+1XQ6ZT>HQxNK(Q_caL^gqs+z67aOKYFvCwhilQ}cSqS_YD}!YJ zEtHmkiyBJ}E_jkF!$YgVuh*kS`p!t-Ej&~vzLisD$VeL&(u53FdR?U&_AOU=BIr}1 zcF(1A1aaUZ)Jr2UST5p5kC#}ZZ#SIH$Ux|hMCwr%F3Yg<6l{`WKJjZX*ir@j|E1 zMEzUN;Or_P@pY>;=U^p-p7AT2-Lmbya4^;5hkm-fy)k%=Hb>S4>A!Bn8FS~}QP>xu zu}sB!y`uEh@8lm}gWDu&(Ji9oCvi-q-=5}tf zlCb*vPp!?C#H%NU$JF54Xf#^S$%0&4&v2e&v4TB5lBWCj<$E_d`Y)G${Vh2|e^kGS zK`o41ver>+uU3A!?LBq$O&hM5&8<#|5dnK1)T8f zAdw=9v&J|>N42e1s{Xv)M1p4)LypJ8bTR2XEo_EaC|nT!F%lnkt`gAC8v84Jdmp(x z>*p+_u*`j*-tO~RLhP|iv$3P!r|=?Ex-NE+Iw7*yGo@;U{xFm}U; znVg0p^M?4l5FHXH7+nd`s_&);TWB~F8VF{-hyKC{rh+08dxrq=^G+;c_YfFU`nR<| z{`N;i=^+q%K>x_xK%TcFEQZ-|Ii7>O)Uso2eKIF?vQbeh(aNn{>$Kp@ML>Uf`bFSH zu=<&ah?uzRQv^GP$ir<%&UZRoL$PkAFGWO~x-GuU3;*+{@?Y9o5WLgrqca|1m))Jy77%#r)^?a`vTe8B`A*XEOqi`+0{?L38~!+s z&@S9K0YrsCU8IAF)*_632c+(3qJ4(0PkCf6)BX$&+E@Me z7I3o2gy$SVGIRt6nc<+eFcQXC@;CxsGIf|^n2^rob%}Us5@W(eI<_e*FYe*~3qnbO zHIjAViP3Ml6^4$1gqiw3>QzE=Y-A`1h}CoUF@V;~$vUZ7@AG%|H=@XPt9k7j(RZdm zKtM)|UhWPGn}`k5(>!~}Xa9P6 z4MRu~@o9yld42htM3{_^U{%dxTq4g(RpyKxLtM)>A_6;n~M9zw$bgRxFaM6)u_#zQkm1MW+x2-4Y>o99* z7q=!))L|pW{jZ<-OtmsjG<_vpA<1%;M9w}pu)PhGQsb!qQlWbQT1!%cWYLtbk}6 zm;=@RacBDEH#uz`-bMI$Wp5Ak$D3f0FXeAm=2jpN&~#Tzpn7c6O4KywI`o&W{OD%M}41l&m6*uW+f!nO^V_5;4M2&3Se;QfYkYk zY@rj|gGmIOHG|@{M!C%;|L@bN zEmVKs16ju(R+_){Nu2rS%iaWPxR4-oPaZ>rV2Eby3(D_8e40&qnh{4$w}}uaeXs~y z+gJMbnV(ClE!S^H&q>1BM=5~N`QO}IAVRXoy|wUqmOMqT^Qk+ov2fR>Lt&z54A4?s*SPH4A*RM%StjL*bq*p*!>~njE-r&~nI}VXZm*X5?OjGyY`da%S6y$nuv>x3dm< z^7a2)akPJ2i{;wQK){rTJRkFJCoQHCS!|0os-Fq}+VyTKK-=c~Ie~vLcTN)lp}wAl z+q7aCy3bpd1ColEl1YAjs%BZW$b=OvEz976CQ7j{IaL;Lb^uXcHG!WQ^&%ESkR3kv zv*ch8s#nK%TmM$Zjt&XfK!8RAM`vVVDzgar#c$xFa$mjq(YWJwt7Ifx(s%J4>Q~(MNwKE|%MRLBS&NkNH=`0D=K4`FD66K~Z8?6; z9MJ40rnAZ%Q*&@)+9VsZ=709%#_X5rHD9O(d`RN z_maO7FYe|cmY!+DZ6fBV;xQP_^5!ZH82e1f{Z|FzU@EmUc)IC~aE(V`FMJQ1*t+Z6 zu&tux;1ZF=fPhG?^~MM1usIz5o4o|Km%?_*y49f{www7#(vzyVqB_jj%5v3rBAO*& zpg)uZ@p;)lO86EgX{WHYGr<1@8v58|d>n+mnZFY;|I@QZ)5$v-*uO=MokP3ke&g1y z&uE!0pS%-H;2xR)TDoh4`*uC<$eCH!JO2{Dztz+@w?z}y_cN(U{Wwx)^%xH+P;&-W zUEPeFjKYU*huKY|S?!Qu;QH<$)+bGMtTK^Lr&KR>j6~(0~E>EE*r)xX0)2wOEiwsGW4T98S#?JgR0r~@0lE+{{tY%G*)pobXdWv zU@Tm@AToE#xAh4ujweYdL2Ma~0!DQl(ytXjxx1}ve!|et&DElim!;&SRk`B6F>`P3w{8>~-!mMt~UjajTZtfUt&% zkR)q_s0da1rmzfjlEsj#PWqyccn1R#icGJvj4FKk`?EZkUwi40CS<|D99&wF#bjtD z*Ivs%VqQok_@VZ_I_2?4w)TVj;E{j*8+7AgxbU+<7mdhKc+YkMmu@15m*>F+4}(I| zBDuzzUi)XQ1Q=vtQ(fAGo4F(g^Xp86J~VabnEOYY)fGaQ`nz-CZ-qU|iy)!QBX?tE zRnEepN6eK50^b)+1+Do`)x^Rli{gP8_CE%V>SVsne`D|EiQYj2g-v_Jq00YFog2PPDE_As`F1@j4f=DX)zCzqFLpSIVjxpd?&>$YvqkQ zQ2#D^M%M{bnx`gxyNtUraY9#YpW}}VU-nJ5-TO|vDs(~o8I4$Tamb&%a((jKW-ev$A*MH*^E z$LY;mzIyedX?^5$(!^>uWvOcY8On)SIlozBX5O(pm_#4l`#!ucY>(NWdMexF$~W+B zImS0+12vokM(++BGCM;LHahN%D}I0D)*Lyv(2tYy9! zl6}{h0&S`^-#ZNKVaUmQr2#2FYOtxOVQ9tNpZd{=l$uV{E)~UJrF$dM_Y)Gr)_(p_ zB?&vm{;JbU7Qm+^9uZYD^b`**)*suD;Zy2qUXciom1rO1Zgz#hzijHlHE8LJj6M;% z;hHjwU~7+_*gC>~rnUW)83~#YAAUQi%EYzBB=pjvU++Eq-^Dby+dfx!P5aF)_ z@jE#5A=5e}#UKw<`#aFIY2Ai)B634#2qtE~+1F=RW&D6=*Aol2oh<6UC#JrAxXBrt z7E5#gX!B-jiQfzGzfKJ*ouiB6Sh1hv%VBkCkbZW4DC>+Vl$6JDJKlPDhOd-vtV6Ocv+)BDg^=8p zzGu-Q^j_%TB)MvFe@V%pETnwi=;=9tg8f%V$591R*`aG@Q+K1BoSw9n91*^5)X$YD zQi6Ot)=4GR2XxZXSu#8WY6!jTDw-?wyOT-?T)N>0{|j^kZWN1dr)r(U;<@GAv5q_Y zLkHxuBAztUT;F={i=OvoryrKuP8W#44_SUW%qDdk@iwt4qc2~Fq3ocmlb8;4$(874 zdCF$^4eK9#SvsvNVAuC~&a*z7qGN-lY&XfX761jfRUA@zz7v5?9a@7AMi07n4eHwi z&t{`i^2UCjW@upOt(w01aQJI^`Xm3$`B5rm!NkDj9Ri5z1s}uXb%qbha4m*}%vuhi zWqF*Fpm`4?*SatRVjS;}^Je3oSU|Zq`PW{IF0MkCY}ozq32d*)=>< zINv*ctdeWB#G>)`-?M_0w2_T`V)x*C@@Ct%nei9>4Vxp8AIF!xL zhV;_JjP_C7RSJhzQ;Lf-v?~*zd$$JdEI>z;Mea9kX5Dm@w2JCiykie|9O`s@rv%zh zsGvWj7>UP(lr2I}O~idK+m+K~_3s%`Ylfl5(G91DzhC>WMGCYG#_%D(qV#L2KTA9n z=nZ>MeNCpi-NDuhG=Qf2ibkE%;=ccD&h-3U5MDHAFH|n%BbMCz3qViPl#>f}-ir<7 zI$yqnG}^-8td=hsTw$&-MmJyod_kpdT&aV!(G6xCk^5CB%&RFrar98)+T?)z`d(|h z8Uy2Gn$VDhv1>@C=D*$4?0z z&`O2bcZ7uWsXiKQosT^zxq&XYe4tf<46S}9Jf|6v?#cTHR$#wuW|B8!JfH10ez}C~ zD)puH%ZO$ES1+TyVPyj_km@W6qXY(Cn(mfF!~=)^^V(yl3@46($t1C#8)p9th5dCW z4W*LRK_tbyvD9>>HtbJXL%(|t(i}Vt8j6IhA9kLC19I(sew{cuL{1rC8*oJ{=dJcc z34@Cx+FEBi*4el>&q@9oOjcM*0lJUeltOYm!Pr!d6ZaiYBERiLOv7b{t&o06du!^| zzartDb-c!q$g6;7&FSrAU(514(y_hlRFshEXk#0i7(a}YwP?^P*W5|Ll;wjo!D^Jx z(I|;Z{go&&;(USk@^a$^IJnV$3Qj4Eh_Tl^GPnJKbSw)&&M7v)X+uWs!e0OArKbgT ztEWK1otawj_p+BW9lh#A*=LKuc|}8Y%v53T9hAL zsz)+K){$0Uc)fY5weHcMw6%$Z7vp*Y2NUtEdT;Pd?`Qpbk`vs)5@or`(qD@NIjN_^^#WM2c?0@AP$4l8Y?eQ%V8@2+DgmZnIxe^1pnE zUyqH~aWw1Z&rn}~MtCD_*pi~GDr{66PsFSKu(U#WH7<|%;#?q~h5kc@O1pUq<76;! zN`)ICGBZ9KIVHZ}5%~%Jm#w2O*-MakOpu{tjU}8eWv9C1de)P|8Y~sP{%oNa2m$JJ zveD6=xd>zomuo`GoI7|z`Cq@GlbX)2zPMY$R~i+{LV5CfkygZ~Vasv449C>I?Z1-Wo5SflFwq6*kErBn&W@oS=mT}%6me_vH`U}Z409I}W zIj@pp>TSeVl+)io)iJDF@eMSu#g6z(eTUz?&qV`p694(7%SWy z5(56Yp6>$2(H-X*5)OY2+@_}2i&qC>Xcem4dt8wm*al$lG$rl))V?6%(S$cj5GNOP zmP@>G!TH=f@UJVABggi4{+{@!Ja3#4|uW6tWx9%mfep={$+`Q3BR5{rXJl zJgF?DCi9ZE;Z9|VdOk*HCVI`|0U;Axc&e^;8{hYrUmb`^eveJJpC5g3XR&L3ycJvm z$72xc+PhUocKY#QEZO0k7q0#vL{{l1TdG(cZCKX1gSSW53zME1fU3$XpPd zpW16ZW@kccJMDarJHs6EE)7MCw90mkDFj9)dNULLP;=@aH2PJQhO3B1M}?F(W5hkW z=JtaUkFD7>{t{f@`Ouiq7G2!FymLkCuJMe^t>r&2SCZOLbG`RHm@mu!qAwS{nRu96 zLom!8T|Wx8fVVCcC!5+dW+%)lgd;e9r5PjaJ<${*5nZasp=hW4X`j7FXfNo-b()a$ z>Zv)rn`IjNwDo3zsH8+&@$dRll=INsMHi@`Iu2e|D^lHNaU=xXm);)#iSfJXj#h0- z8!Ifgc0)?thtmHv;YR?k_`FH8QiN2IG!ejIT`*R^^mVcI{^Zj~Kc}C-JqTw(XtN8_1qw zrKXxj8PDJ4^Oa&n55hH9IZ&NwCT)jIY_i{fc_l6`67~u~lWKxNM!Ua4U2#i*fg!NJ z4H)Vdvy zOR+Tp-nPcigF>;ol%K4Qq*xR=B_UDscTHK=>4SHCICi8`jPBXH44s1G{uHXD# zE#Eb&)2*bLwaA;d$ZPM>|D3h7rj9gHEt`3GT`IHI@>})mmgnz`g*CfEHgzh$yE>II zh07e6U8c|s_ZY#wS~Pb;6B9KNrvL`ZE0dbYK8URZZ>$0mvYR4C$LHY-9{MV&8Q2SS zvKi%th5i#q^g+sDui29;I`>Ver-933g{{6@Kbl3!dG+it}aTC|4az`@Rd0;L6zXdmop^ zwdDEDn{^MG3}Gy>o{PH)w92AuKO|D~v@Ik+*>}6sPCuH)2s>HiT#uFfWLGC+ZyLLQ z?p4w)8;2d9Eu9m+oxgwf$?_|%Uv>0(m%UTWW#6iXq3c(B^|YzRUe(^Re*FNRr+Z)1 zzU2I5ml$Uk={M)O?CZ8AGY5FGNhR;wfAfOc<^cTH7|Jni)(xlbvYyL-wV6m7W>zrZ z(yS6lgmg@~?LfWpX*om;n(6WI$GX1G5cTdzk%A8gq;s52V4L=uQW8oAUP z5N*JhTBCD<*UQ7EJlm$=!Zlx_j#W8BkST>BpJ|g5yGp+Il%(S2Rw4eNtD_*hXldU< zopu6N>+RS>iOlYaY|6_!c*I5+{{4i~nKHbw688DMmUqyFPm5jFJ5Y-f%%7S%b-Tg3 zW05F(IVCp|p}%Z8iB`Qj1W$&@$`@bvx}Rq7tn=*ZR}iKD#+lU1^|M@{|H^VnZ@OLgtbj`bS0EobTSSe7p`@9o; z-+dfVQN3uLwvVLyEcbkze8pJ2z?fN18k|-25{r#%QsK4k#N)donTP)MF%VAg z6+S^RPhqAVhQH)bP8xb&qjksun}y4wN`N@*YwC!hp_#fHkuuDA$C{E}TD+jR3IDE` zx#fpAb!GDfhC39*#?DveRt2KBkBT#(S=1g_Y)NOS{-#oJOBSf&ZK+N48^v=XfETLM z)oJ&eOQ)uYpG9ft5qz(DVoRE(i1Nx39z)>tvcV?;@_tuOvDP;Nr60F%Gh$$}>^W8+ zW1vyv9PN-+DKMYdZ?o@m+Oi72rC?thSqr)~q~`V83I1|K4~jc_1Km?A|FrwkjUu{6 z0TmwAF(6QMgN({skHQPe6B0-rWEL8oWZ;0&$Xsrm$^acJ9o#jnX(HJ!n(*UDhKco2xs`ZBGqAw3HufJ!1>Jdu$eHwB zb@s;3iaZpTo_fiPoOQEUpB;<}WeiG!&c9|+irt7_5?@V3R+cE9Hfq)#tuDPX4f^08 zE+ivU0?FPuI4R81++6`>3Eq||eq35$QQz(j4#GxS9M%4jnBi9}_#E>vqyPTo0|gQm z-!K78RjL#7GZf&U*neJdT^~2fn=vq*5QBl>4k_8}IVw)1*7aoqssKbW7oAtSU zvDvLu@Nz<`obW};Yl0MH$E4T#;0OYiGMr-~1J4{mNE8?NV5ZbBBQVZ45bmAx*mja3 zrN_bNM`-AxX{^Ym6Oo>ygz(9@*z?B>ArIb|Ij_xd#?AdU1Oa&#^|-tOJ8EbCq~EkT zp@yJ4nHArQ(Gtx{gT889{k@6Hn9~W;?3-{p;rqS3zG_}#y$~W2f#gw4%bot#J(WCJ z?fu=cj5O?1?madN*gijq;Qg|V$RqH<*MmXEtlA^SSj3B+QAPPbGA6U4Z}YiP9tZ^l zGa(NNvS0I9^P3A}q_+f^S2)nr;W+F4IFTcTmu|(3l#T`~db8{@Bli4A_f;^23Uu40 zgD;&aC5L)u;B#!wMok--24gC|r1oBf;j2&x!1a}qluuIrDobv71|&ve;Dc>}iBqh! z1=u^5Y55q@d&}dIw@E^cB7eIhS5k1?ljn_H z#TCO06O3Ymo~|?xR~@l#dqoBvdocKjcbj)JeKw)|NT9rTc`naSH$ZO(7T2c{j;Z~0 z=OHGP{la%CCiyh~<=isBMyJBi)R?a>BG6tup1C6MKs;SEH}76x-R|v|anT1>5V^EU z?6524S+m>WrWFpy_F%Dl%fm^FE?x8FvNh79#&YR9G}oL!EeJn%Yz&o{C{2bh35=$` zoBE?xP!{NZ5O;2IaWTlXh#|{k)l1{6*cXFg=>ci~ANmuIG3|~E-u^z4YuqO)SVdh2 z`xAu~NSA@bHjCK>ET7T3e-Rnl=FdoQHBy|xF=U}h*1#erZ=oZ9HNb1xFG!2xhIwlD zW;+MC&OOz*A@|`a-(jh~{C+3RVbe;5tN%29-*p098hNJgv0|PNtGd_z{M$#fdRD@v zZFLKc#u-Ch|*#30A@w4aZZ8Yov$&xa;^2-)D!HqU}iz|Hue()HROc5AG zP0YeuHv;|}8K3<-e&t!Mt9I|GsHFa-z3VnejL;^#=boq|S!aXm{?sie@^BJ6-xPKN z25=TETJH}^oeFP$Kb(?51#M=_CiU{)E{2vs)IU5G^uN*;YMfcl5FaH`s&V%>8st{p z`!t?umk)eX)~o$$R#&!_Y-VN#rOqqJ6_NPP5-FJA05v0Nk*5G}ze{2MtJBqtCL?)P42)@Q zJ>~1>Jz5C)U|iiUr)gg=gu3zG(z7s?C$;=I!1eZMzB-R*cODbaszN99iHl`BGXa_f z;2v~_yLUSFFPIm(S)P`s>k`%x%C_QE&tf^F1;jw*JU|2GMm9K+k|QGZ$*fdn&T)aO z6B6+(=UFP#sbk)_bwwaXv)O}1iyLsEmpbi`;Xf$K!`yLrkzrpU7b9cIv0F$dC8L`Pi|4^?Qx_iyi~Z z_XzC}2QML%?kemvJdq+wxu*a}+Hl@f;vYYr{8Udmb2m;*#e`uTzI*7eY zb-0R)fwAz$63>eCd%Buq>@_A$q&$->=s$d#_ipePuL1r3D7hs;FmDAEq$NJ0 z`f*@tG{5DY`4yRcuf(qT3AK5R;}4IJj0blXJ{rYii{#UF+RRlbh4tT0os$oC3OkDI zwU{gz{ooW@-|jyVo7+VZDkrLNc*|)qMt&&dN1{^3{W6w@e(4X{-0^8?m)xkEFJ)H7 z{p2ZqeSfHk`QDy+k|IP!BwD?p0cZtm3tah==Dhog-V|gm2*=9D-3dqahm=ORH!+`m zdi;wzO~3uOQH*g)gUZwOm#IJXf6ivfJc7K?X%<2NVWTtQf)c-h0|IAugPPg)kz0|i z1^2JoETQbB!?TzxQmhG4*U|%#pRq@tGy$@Nf z33!Ny^4=4}As4oP{kp{aWnO3jb3O`ApXT1bQv2s8l78$2yH}n0U1zGp=cu#Sb?_RA z32I)pz{yp+2jJ0sSi_{pLznQU!o^fPEh25KS^iJAWZ}_*P zlO7U=b?g^w2Z5W~cJ_TeMRxjWDb{aipW>b{Tc0D?7v6UBRWu;<9TOk@#cy%c0Q`Fa zi>X{`epgPRu%)NhWEmK32n*WH@Yvi}QA>dG5*y|7`Q&18&ScY}M|Gdp!y|NADaf!( z?glMK!KU#Kcva#av6`KR>ZbQm5Xg0epoRk$A68 z5#e&dL{UM2o%kjwK*RY#+;)m)oT+QI@6CFH;KHh`p{o>@fi5aC3MpVeu&F`bo#HQ3 z`q+zd7GZ%A8SCP702>lp&gWry*s>#$WetVweq+*fqQO>#>LeW*%6L9P`=j^ecnyNb zP}0@b{5vtRC6@6L9Us@8eHhI|79`~3li&ZtSn z>owz68+E=8m7a2)q(8-SGduDiFJQslsxZ=5N31HR)0k>xAs_QDSvrd(29pp)29&mG zJnu4tD ziXwohKp0G&yz`zgJc@1FdRW-BVDYb4DvP%Sf{bTmF#t$HFzA>pqnX2X7JZ%bVDXSI44~Trta95|z&jB1JTjV^ ztsnMnR~mHDcza2+4;4=pM>5C?Sb)Fg1^2>WKX8n?M482I-)Q3Ps5MXgwD{ zh@1Kk*|?cf&C>T`PamzzOgmF&i41+6rhZ8v|J`grwq#g?!&(L4YKYkE%dsMV`SGTj zq$?0h)RK*V(;OB}QvU4E8wSOZi^IMId`)K<#AIwqP_d;xeL?I~7kJ;MyD5cRt!h6YJfpmK!cA1^h; z3Yk0h;>ZzO$=~nghANSn`M!Dk_AOXNj>4Rx#dhHh-6}hvL8pKT&QIod%H58O1=JzR z+PXJ`fJH;gJvdqNqj@VCAi$X2!S$}dXwMGd(RM&c@i~o+T6W7o)9V1X2^ch8TpYi3 z)ONG#v=LY|B!AzC-XB>UeLV^bfJnDDhjWz;?SV!Ermm0t7fNZo z+WQ}R+yy~5D;}D5i?u3fM6YY_6;v)*SXj8m0(~esO@d|$o4qbRm1=~4x=7)&C=t1y z?~%_Mb8B`#w$3;O+~|f)rwl1f0qNt8=ienxkiNdVw%nWD6ToYhZ8huqf~cXh%-D=- z<|}x!&oDj#7V*BcJk;Z7uZU$Mo|KuaF;}D$p)vjf_l_3!#nt?n7L}_x*3g&6oc)iK z7LZ>lOZm9#zn>oZPK|J!(1wBw9;8?f03&3;ge!Z&H}Y7wc>aV}#G;A-=qT)7?rc>f7iIRTfjyBd7mK{LvD4Yh(*f%DrYvdw&?#ld+V?& zpSEq3?nb%=K}Av;X(=g@ZV(h{P&yaV(%p@uboT<3knV2jPHDbb`g`8@d1LQ=eA|CK zSob}7&s=k!=XK4@G98OAdX}%n`R1_19^l#NCkaOg%Z}+iCC>psb_Tp=qQK+#yvEt~ zxEwYqyJ&v`heWDvJGwfhHd+%GouK!1CqJzTH#fIb6ZU>@3yD_c;4k6mH8S(sn$V-H z=jLONcbdvLO3L5&CtZ}6)i;@nw}YADOZ{xqP;m5UwTt4&QQ_0&@tRbM0;egf>?0`N z50hZ|CE*AhZW%86UgyC4`E+df5sOp(5})^Gg4juUsY&h*%JS!Lqp)t*7!w0C{X@8% zjfPqWPeHMXx8gPSTW_+@;Fq*j@)WB_GIp{ggNXqF`G^fG_4H^ujI=+DlUkwPu_-xQ zVZHBB0g~o~wnKyc7J*W{CF`@79iDa&zRP-?mzdQekWpgb#hvE;M55!t?u+I+caHgDDVgiq_F zH7xzwzS7UiSR{i?djXTK9^fkRP$4`tQ3wa;R1oOFAEQ0qqR{?C!N?%EYpqmFZmH`O zkL&X@2P#N`5NWqbDbBk81D@8Yhs*}nN!%91t^T{M{2ov zL0vfz(7xp&vK4QD^z zyQW*$Xx4xHdPbGY*YgRV;1S@ARa3omS^lw>RYm-v;RMHYNI(}zr)YCT9q`%PGCwZhTjmK|>`L3k6ICL>@mNO%9 zAupL_S15qPiL~0%MVM;N6zEh`d7tp3EI@%Vn-mCj<7EuMoP`GGjrzl#zlWcymhPMu zmAA|Tjmj<`a}0@gJy^nv-9ZA98pKUzW}I0f3#&C6yQf@8!{T_d|oJ@ZzsSK0S_;Y zJ=kI`fDl5S$nwJWPBZFFu2{dO0Ubv6=Ud&beT^SC+Id8UuY+qX(nT?J1U7ZVwzK1- zv3Xzt^cw?7yJ&xZiJHWNq4c1c2XXAa z`Agb7GST2`neEN+=p&W6Aem3hH#*gthh1?7U3C)Zm--y+apg-I+w*xM>Z=}lg{XJR z`LiT|mj2LpqUiOW3AWiRa+c+69yFOtGSR+4VabVmP#=Vj% z^IN!i3c+XcuxV-*|Di~v*XD`Cj8RYoJJ3P@?*YdDe`xZ*rSwL#E!A%x1ES#9UE90M z4GOc*hQnEs+d$G)2a;YjkhojN0GP_r_q=>p4LFyYj*dl+Q+8FADwX4u(e|)3jhcb` zk>3?i>c+!G?(!GhPZS)6B}oceJg&*R0C18`|0)+>2&6M54dLxE@nyRvzRR@U4?da5|^|r>L1PJS@RBpTF+cmS+mF5#@7kXe>0Dq|H)LMhZr}zyF zEooCKd!Aja_a{?q^dOBjypv&s&NnzzzMR55z;n!2dn@&VEvF0@Ot>m-wU!FTy9dUZpRr7kzOQ|1$>Mv>RuW!I|{z}_g1c<^)t zbzQf<#?|$5WlM*v11-2Yb{${@6`+Gjkw;NP7&vc@BuANu8o?Nis-&c!-3z)LBmRS? zla+U$0JxEQ0a22EH-Mw4y(Gz;BKqSePVIuL#m#*W(iHBbzMIue%5&2Jt`!wfQv;8) z@d;!)*GWC6R5c2z=RQR@d^CbHOOr?!a$$K^#T|V4YuafH!lp{AU8Q2fUnW%f<}iOpw}qr5@RO-{1Hc0 zhi9C^vFdyr0G||WH|((PxG#c;|;QQCC;h3Mea|EmwY zrXzHdpG=874vb0RG#XDtZK+?EXmr!4EO9U_v^n2ci_hk=U%&BYQU6K5ruodm^AQe zUQ6K+a0QZb15xbpUJFEwg0!DC z6`k%ojd5oP$4wf|9MVN`?ZO-2&nOuue-eI#annE*#o&1x_yBA0#@GIMnih*2Gvh-8jOo^S?JYpdGJ#7I3@jx$5tIv+Bb*o{)zY!%O zmV4ik^Rlh+fWG}wg{ZKwodbuS_Qr_-Hu_2PF735#`P+D^vWYyfPe}=*ONT5z zyN^m!W`3VohAD%HUdLw77td+EFEJZPGY>H34`Pl8Hod9tt{3mg?G&}Wk=Z%1OIxlA z?bryK&x5Zcj<%vy;1#PI(9NUKraWI$?;sIxNJBc}PMpA!B*u(08vM&n(XjY~U6*862&vI`K_lEOm`{VC#c34dwBtQ_6WBR(tygB~ji1h% zNxTFc!CkAj&m=E_fg(LR|M@uKKG{;ijKWQg#eUb##f^SCyRL5Pr%EflhpcAjn?HH8 z4|L)ju5s#Tm5x8-wYCNM-oGvTMsoVeuqo`ex%o{Th3npLulwtC{B(4Fq9xVd;LPV# zJI)U;rbFP13cD`|75V6>!?|9+FBZaqL$vCBGq5}8_W7d z3g^2ok)4VG{gk_qon9^m-V8>Vg~^)>!(-0tVMP;{>BwfA4!mz8>9=r1%{h2gjyU)5n<*#u_t~pxMc$-!#P+P7+}>)!dDv`Sci=imLT}>xCvQ zK>dx6E)70>rUqUoI~~nnQM-GW-UmDF`g)6 zQ3b$61?fGuVEKB*Yk{=+WwK3sYe@rAsdtSWiDyWG^}XFN1wf36!k)J>0$7N~KlkvX zz3-65rpj-Lu3U$6f`*3^zK3eZ>YQ)^mFg54LszAmNkrH}v9oWyBUY90cEP5mG4uxG zkVr!pU#ZokhX7cP5dxTc20x`R9DlK?oLa>gY058ne|YuLYi65vIsZ%ZJ-7nWD#4Lb!+HnMSxLu;NSEw zKe9prMyy`Ybp5?=MOvKMCbXn~IKgo6{FQG{F0H|?_C#K`8il3noUTL!2H6y%q#x9iLm0z0tI;K-^18LLpgVR7ldKppujDCF0UsN?SWXBUG+@68Kq9uCYpOe20+)dou%NW(j`_bRw6B6crY#PL}9}(oho()rT+AdTFo*H7JOY?rFc76jv)v zG7Q8x!bP!*t+Fwi;)^evGeMmh7r}lAg(+oV$$ai?X+C;Xpk^Bl)5c{yJJtm_T zMwMZ%WbhUZU6y95@~!;+Yc9D;SW0f8il@*YKYkPfWYLKmB5knUxWN(lS=g@P34PFSH3A zAuf40hGK?Wvk`?6x{3QZh7q0VaMlRXEMESxee!2Z(2#f7$*B?u$M7D9oZ+0_Ym9_dPA{Iw6<3J)d z!)*-D`t<1=SX7C1#cOYtoX{KbAhzj>Kognlod>mont+MzyQeC7Vg+O`cAX14w z@gVm67{Mwj#ePrQqx)sYboAk{$(l3$K@;?2bc%g1_8#n!Ik@V~n6IXZEDFtkSFZ7q z56_vuLF3zW?X68&=OaPxptVj4Pw?(GcB|vKtPZ5yBf1Gc3eA#XFAi&FjKXtI`_qML zM^E(O8;_|6^tjP&A6KzkLA7DIYy*5e1=Utx0)lg#*>2s|Tfx<%-fw5&PXRq8YK zJXXg`2o@3MvLrEAR~Y#FlenMn$|S#6eA*ayFh6R$%ODVgy>0&MNw30s&Qs7Ut~nJ& zge}O<;X>!^tnyKa$+ChYxrxy~OF}ooxl#mzaJoej^Pcf0Mib$O@->N3ids5gK32uf zv?{apx@jU6tsDua;p`lr6JlGC`Leq8hsi$M$|8yuBvLt>9!vt8DyyBQc3i6@GHb^l z75*cM(VNQrMj4T{;O*6adpm)Cb)?HGhVt)=ID)Vjz1?G6dOt?i;5l+js{zZfZr&AuX6OnFK2i>X)P zup5kHkk*3p1tz=T#&hX?8reU2p&G7{d`vftOE!#vO(ztyNfT#cL&bpsN`B>J9`LsY zD>Txe8<}6z`ZH__ag*lgMT44`A7sL+G$$DK=cU~z%&B}qtRKVwdrmk?gOvO)n?ZDz z6!{8`*<@0-DH*I%{6s-(jHD*@nc=wql*y_T$N#aQ0;XD+1$yP^S=2t`OaXaLM8!Pg z^``H|_Hh5FJ9>j7PBRoLeXKRMpt7e%uyw)0F#-hmZ6Y}XhQA#lfuk%<<55Na!nq9p zAiy|`UkVmwv<>2!G+^N)l-D-y`hb0#gD?<|uzuqDGBdi2Sv3J00*}A8lq$!w0bc-K z7_jXaDj4D91~7s?l!Ta7msCWl(DnzB25FaRu0viCP z90Q_Ao^{LRdv2IBRbeXfPu(tnl%a&z^?T9FAe35YMkXF(EUVcc=rV(R!aBd&bNwKo zK~32*wN?QI&(r5isNL_09LAZ$3(6uv!>6XBYk z-ZlJDt$;RW{hb%dsGRw!JZUL?$GdpF_c@^mZ$#$NzQQ2}9s(IGA%YC(9%|YTQ{>(8 zF%%%MptuVguV0bUAs6-uyf$e9AiQQQL(C_n&R?{)$bLb2wM)cvJZ<1R`&sVukW-#f z8(}%d@PGs3YQsd~SH78Z8U8h9ql`OR@pfAom^j*(Chih=D2?LoG#;nQduGMBFnJ}C z=c&xCJ$)4d*89GMOi!Tt_{w{u!Q4wSJDo63$0zsmX;XABk|a1|TOTTRa^22rqeYM3 zYPIu@IydfO=nV$;jjuMSxTE}h_8W#|Y(5R?k@WmR7t0MNFnY`j+T}bK=e!<(OJkjL zVLb7N^kS$WX#3{IJLG9!>l z6-7JU)`lc-xsOrUWH|LCU<3akKl0hLmQg)O{A)<%Pu@lMXv0C!+1kZgm7OvhL`^p{ znRv*gD{jDt_<^_>x2(4fm1Z|lHky%aV}Ki0Cb%(h%z1h045DpSk;E>rpK9K6ZpbS1 zAPH`#|7dFr@_W`s!~ysYEr{oc+LvT?t zEkWx7$dY3q{5HqBZW)E!XNh$T4;N+xu=5dsi6vD5CAa1K8H$xpRjN}vz{g1D z#|o`rX79EIpRNEzdXY|AK8DSFGI$hJN?QoG>m-g&jU~s3sQXt@QFpsl`>4(vA<{MIJ_^kfkaC2?QM4Z>|4~3<$c?7ue#sWlCu+|Lo-KyPCWj>N`98$gnCqBLruK7muzDwMBrIu0f*uq=pwg$Xl)@jn8H6Qvx7X{8q)GMkx>WMr4;D*;@FjkFzvu_*|DVy^Z#c&cCr}Kh2hgn zk#-5Rk=r`*@7JwXr6`-jwCN;EIxx<=(GI_9Rbm}E6kg820`Jky=-RK^w=QOJG<(&k z(^oqE+6)XC2iAYr;z(cn^q51H>0X+AOd&QEe_18xv@K z+ahIPM)hwmpF;ad+q)Ea=X2Ix->wTKY%`3$!-TDw;38QR-A9CrCS7l>r+`jD*uu;O zf6j?9gE<$*F8HTzyNCmX<0j39Xub3C3C`vEi8eR|w4T zm-G)Piu%4A<-YPbvjy5IMc2Dv-Ps2@NvON1O_#5^tY^P z=X1dlK|>!(!-va9T9&f*85wizYrOt;oo5^HXc&Es?0QECtF1aD60*+<~V<7iS3<2;V$Y@tK*(Q|<97+{eLiYKH{C5XCZX(T^`VCYDc zK;F@VkNXY9L$);C&4CB7@&SAD@K*nbD zq`jkfk2|$}m8RJ&=g(p$hDdjRHfc~?5m?{}D=}J+!&IAwGTE$dEly`-I~f<@FOK1G ze)WZI;qCdNmlwSiXW9AFB#Gv4X1X5~hA#gB5JSx2%8U2bJYTI`FHq=Pa zaG!qZmzsIp26$+@i3Zlby_@$bAq>qG$!e#y()|Txn${yB!vmf_NDQkgCn5E2#}}57 zn90Nl4SHWs!XD;trB$v{pDkUN78dEd=!Q0W21j;=qTdTC=uru-xBWIyHWjYabEr2g zj$||kyg#69+5totq4)Rc3lH;p0f&G#;tHdDf?>*Y5||0GM{wUR$m{gO=U%i=P6aVZ zg|e2%f4qMG15SE7_T#{o)>Jqj7M&VRX03NKE;vRGuErxG!}qfhhyfqpjk-(2kPE}r zAH{)N5LQAALdxJ!?zGr{e*jp-@2%0UIB3Qo&rK_|H;{DaR*mR~Q;-MYOCxOaB`^N#?eAuN!!Q$djYsBRx}X`C6kRD0=hr zjY&N^wH9D?h7pbm{-*FH`T6XuFK^%}MsnNe_q68<3;x$OAmXSf(bQ&cRhhl7n~qh9 zJVxY4hS8U~pMa19D(KJ=4_6s2>;dA;ET*D)`YCxEKhmmuzW(2+VI2r?siQrTie^;6 zas!-p_5CDJK(I{}v!rDM)TI_G=1dg9@{gT7QUac7@VbNh$d&QqndK0tuxg^ua}=J_ zC+bne#@WjL3^ZmD)A|(w$R}B(Wx>lCZjY$xaclzwEIzGSkpjl|qq#6 z@QimW@;})!F-%2UZD-&wORS=|O5!g7x?5}I@|di>=v~i?clA9fs^D8IgsVC_wAaER zhQ*vH0 zDky{+i7INUyGN2$vTU9_Pmfs=?&$* zfe92q=O_e1iO)c)AO)|)wzpMIbCc*F9fpVh0F$x8Ea4$TXNQjH z9Oq(Cwf08J{?UY(#0YC81uQb z6placDs-6yC~0ZF=;ZPSj9~vGyE(&TH@N7?>$4rxIr|o$#Sp!hIYOT^URz;Qm`zrs zH5Sbj%VCMpVx#UF%rp;lC({CTR1s!0sx2}Qu(WHft>&CTvS)jE41OO}BCHzK!?rB~ z5zP1uy%PUZ3)F?cFGOKv79g~8M9WTjK7Ihy!z$*u7bc(VI}qIBFLW*G_5M(Bax5); zL&)7{u$+^LFdB$TyGMAy>v#W=G>JUgxuyF#eLo{9_4|{>HNT@aAq343ZG5jdfkYY; z6O#`!O(IXXmw1u6F$UQF3a7-}C9f6Y!k;7gAW5HnIr0v5Jjd)+aLmwqPG5vzIojgk zw%R2b>$o>V22*LH6~rFzYU}0{=3+nh^)XRg``(sTL(<3dM+j92M*)@NIklc|AQRzw zy0lO^jvK+MT>AtqTd$l<^ejo%KM?O88s^EX(fv2YnUW8B8MeSdxl8%Yve8$!-}~OU zLbyr!(+^&@m5^rmg)V>1j~E-HRe+yQi!3Wn#|`z9m_wm<3{Sc&GkEu@9T>Z;PYz1G?x?NkLB{+$@*XjH(0h=I#M~ z*WZqud=7CR*~a!*IQwOSCC2fOl6P+VnT6}Sc}Z#Pi+E|`DChY}jp^fWX|0GK{rLvl ziUY`+s8$D?H`d*?jJlYUmfeo`{tSPG{*{MnGX=vUvj5FjZjUC&DN{6#aeW4+R?Jr1R>IcFGBN7E z9tAwiKYLFH|3Bh_|6g1w|D=on*Hw&|;Hph~h7|5szu~5EZ7}D*ave9Ha~yv^VqCaa zwV`%he?TaHj1*%QV-;f;;}GK#bNjR5mc@Yf77;xzi+H>ctG6Uiwwv`00g9XGQMT=TFNi6H)o)7E9W7j3uFN5xIJs`H*z>=%5z z5#6B}tM|($hi{XfN@#x>tTEmSByJ zFjtM6ZPNyhrHc1%g*?&J92jt5OEdWeM-K$NEX(nsU>B|gAPY&%K2f5;y2_{BB19u>tM%Dae5*!Ui)D;s zX(e%1#EN(m{A?`OP}A}^Ok1_CfiE>k$J6cl5!2iO3u;7Y)S|Xfy6N6fhF%M(W=oS-w5~Y`6?1s zv(eq(L=E2fjnz!jUca--Mo)b&3+ogPv$GM2ma;0$$Je1savlLN8m$J80e~ zXZ_?HKh4+j*NoVOs8xP>+8gTL?JJ60cUkr+G$0zhOm!i=gX2I2_LkR^rmr|C4wm!4ULXh6;CMpq*B)))$kcNv|R2=Pzs+`b;Hi4wVK0FuW1SDYw%kvDS{D5bK}tX@MWg%9K!I4nclgxZM1Yo=9^U%-5#4GV zy>Em#DvQsE!TVQW#q@`ZnEOnOo~Yyr!myaPJ4tb=fE(NO=Pc6m*Qhb)`-Xg81URo6 zCdn?>Vylqr))I6=P{Y8Me<2oW1DlI}d$Wr7QOg&yd>;Vhk2HWqGw~lM0wbd`k=42^ za{FeaK6Lq1w(c?T`*!oP0VtUATMmt`0R62#`^K$*y8Q<7HStnb)T?ppb&F0PDPSq^ zzlDwG=Z_s`r#)d-1{RHzQW%{?ld4F${Qik-3vyjW$IDeZdSAR1ER=7rM7Co`JX5hu zmDlB7&U0lU>MfNjHx;AKm$9Gc6;zJUU?`xZKdmJ$K<$;*KGkOHeXi< zO;aw4zGsYGY8iMX&J^BScvpAT8`Nb5TVzL^H%(;3cGoEP;1R_bZtMM12d>q_TbtH= zAvWQWI{k#BO4cfngk$9uNW!3#;<59);+cSd>z(>`fWe)zjr&br!&*@KcoR!H#l{zm z1NVlab=yNNa3m(f3^d?X>$X(14=y{UTLQmnmymV=7LTRJmEx+a5sC}b$td<9Lf@^I zti*~M1aSY-o7{7u8{^d!qX2}I#lMNi&hMMg`l+ceC<+v zicDecwd3+{6`6DVSqXAH4#1ao&l2e4Z5 zfJ2vf4>Lzf8UNX|BnmEM!OhYhn0Uh6cC-Di9nKjtr(2FgsN2KwJE%(TH)jTg zlWjEQW*@x`;A`qH_GwnS#6r5Puf}8u)xqhi*zWnKMb=~1K@-%_>0)>hQ(d;ce`UAU z@iI&4cfy76?+F`p-0R)w4vxSldhq*Ahlpoq;f*udN{vT?^a`4wQX-&Abg8aWM%aA# ziRsp_A(#&W4=`=+(+)FQFZK@#H%2~8LfS@44_piCR_Am)7(&1&Nv{xas@k(=gxVNqziC2bV{jyH=YVqPYZ@D(#fFHcchndB@UA8BZ<3(`U zarZ;_j{WsFCv0ECntoZ(Q=!wqYEJdKeqOMj=9MC6 zckY*t3*7RlNofaXkraisv|myRsD$75k%y)}eQ`WeYCa`ks{H#Q^ugi>`rAT^k<5V9 z-3dtg^AZwA@P)vMJG8De;JQn?qRW{aW+;i(A~)JjWBOgWXQlk8*N*^oYzLb{)PEla`Z7$Cb-c{+t{c?E&Zq&Od zX^mN-c=S(?b^x)MQ)E5^jsy)at5#U{K`KnczQJJ6rJKLGC^3_1y}v9K*nMbf*KnSh zzOvz8msOyJ@h~ZuF2?aMB4F0TbWAK|f1#wT`d^1Rp8PwwZlV z<%IXYSRoY@b?1HUP9FesV9*B@sQZ{F+5x+YfNTepm zwaT`B`gReKWq%$V(C~vaNEqr*y9=%ARUoEr8oxCwK8HCU=>`TS{RJM{bZS zIrLF@YlDUFIX5mN4&EmML#Cs7XR;r>g)*(%{`Q+jOpV-28zDP>RDTCI#QsO0O~Oar zedH;e55j2_&`vC;gDJI+dB+W0bh~_^**3Z7{7^%dkQkJr0fh&aPoRn`J3RRzFA=vi z<|A-~cFRyw0{fJBA48ELD)(&d%rrNVLn$e&A3iVO*%tR%(6Hy;8TOlwxpJhUd9*rS z?1g&?jlXgj@@={}Wg9!1bhkKrVr&~nl>@hUdCF?na<`lw^665N#1Lz@J0bH$DS z2k-WO*4+@J?1(nUoK*R^WVFhOhw{0zfsncwjmav-A_3i_MJBDO>?qfGr!VM0{x~lH)qqP{G5Q1D|2j|DU7uY- zm3=$Z2?t(6yjvcljIEfjG4SAoHokq67{p}ggk8f-vdpLjh6N8-P2ypb#)=&qI1vIGp#vUBW1s$`I zPI%m2srb`SR^YD?+-!TBFt4?PlPxrk<$dnP@fhm8+U#=?#dAF{Cw_$_?TykNPD zw0<}v_wMeb^s*va9s-{$y)~Lu20UsK?U;WS8$%VRt+5U)3uvS0LeGtTMucRRySC=m3T^va%XIxXNZ8&%myCg3R)m9FqtW*p%W^fb{LR>ngT!(ND zx?Ymr_PLVF`v{z@9}r0CWgBRuF~`Co$|sNb+;}iFRiz$(Th^G*7c%o+p6ZZyw15s1 zEF~FlkY8>AnYFWnp60Q5h2La##{KVonu+7+D=Ab*=;L?t{81a7DF#9}S6!_WfyP+w z?Z+{Wm7M0uSB9)i@F<$YuWXK`peWdNIO``r-6XtN9ZjM(jvhjnTEq7Xf2=-QPGEkX zjv!f~l#EEjds9eZyD|dS>QCv7ym#^qej;Fl!~E$L*eO}f^n*`%{O!H=kQJ&ho&#cc)WPtU8Crd#u!vp+v?P#F`c==orJ zu|AnTwlz5SwZTfqlycH)*2sKk)Je-M1z~`n(b3vPH$m*rC#r<2rTi3pB#Ix!0`_R+ zu4!VvcR#5eXTpg5J`!{6P7pX{r{QHWZd=ZQXx^;ZNw6VCQ_);ftpSgM=iZ0v&o{I2 zvi|}s*l(PKPlBs|WWJEEuK#{5g?#mlK5)iklh1hDq~zh5bp6% zo!ZHvtLfqgs`>PMe7?FV3-8D)?bK>{me%VxXDb^t-^07=aq5461o0mKsweN_#>0Og z=EZAZr9Lz{dv%mcdwa%PzK*t@EA-bdpAJ0v30yPf1H-?g&ZKl7{2hgraFeGxHZNX- zeOFEJG*5w5iX}uL4@-TC_$w7y>KA~l0{(>v@a}htzX*&czi4?%mg`^1H2^)S;PG_^`|+*->LM5S3rn05=&i|8G)LPPlolEAix%(cxO zY_Fs*kHdUf?UTMy+Wa!+1d@9{r2ikR?}=hKtxC=J-S&`SPBo0m@b7aV*R1{l`;>op zg7QqG&+?LN-4k$$*WVYEU5X2nPfCt#@x{^XP=qVp4il?nb^2egwRO$J%U46~@MA5MvZR`Ca>T=z$ z3W?N~otVzIF1lxl86DNwFWw%-U^<5dx@L9GP0a_Qn$MupuUZpX6AYeGiJ+r;Zrp8y zry1k$PJgK71A$_ky3xb<#5c{370WlW)&=^!Kf0MjOcfW4vRKlye#;1w8alsy;x74zGdNisal$BxfT?uBn*yaqbPuh zHVym-q_}h-AM!)L!=Mo*6iL}@;zGwAF@Mqse`J@}ZzkKjXd2K;rXM#!*622WT&ST~ zW@-IP0tM+AG*s2jC*t>vNq>2|&%2QCqNu z6|b6gl}9pT3xz^Ye0)%N{H(q<`#iJ&7gh-Lvs3HBw4g()k;S#7V=+B!c)CRRCEs&ucDfj=+I{GGowJ8b%Kcl8@=;wFq(GzS*k@=O-I2X7n_``FsJ2AJBk8 z1AthNh2X9!IeUwBPt5bmZNJ1^NzW3*P@`CC%w#tkO0~1(fGxjFCKxqwo)D2dL!qo zBP)sFFYA7M73@g5fiX&c1tQ*3p4|ima#aY+7iQH;I-Ssf6g=^EsXZv+Tj8R)Q)A`K zs+Zhp&82ia43ULq9dB!Xwi8D^`QQw{APWpr;e3cj2x=NQltQ5~v9$1K1Bd_Om#1rp zjfFGOB;@TXn^4Ag&7US4BA>&fJW|_JJz3r$!@a++b>b~jcxgo1#rBZQI4V(|!siJi zW*csmrt)`fW$kBGRTqAbt1=o{E%5c~Nah16Y$Wh>^W&GRo6h-P2R>KN&-paacSF*; z*Ov&r7M5Lihl8-?S$kEycXFlJ56I%6&}nLp!pv+KF&$0_`joB`*@SZF9o?bBQ+p|- zfd=*jA zKhMAZ{E{@$;5jf7f0mdNyDwA`$1@T5#cR?n!4SbYYGTH&kN~opqY^S>8u#fo&stHJ zQmeE*@9EY9`r^<5VJEcDSWAe-cTH#U!ZVv&rMgFOP-QdRjk^`pNu7$@-2JR!=t}Xs zT1-`*??2z4&wmO|pLZCH@fe6eqJlDx*v}b3I?Q=Yrt{IW7sW}qG6o$=Ge$T@5rsKhqG5848YY#j zQWejtb^Mk)|EoNCaA!_AQL}evRSYMjvZ_Pf)PB%4-sxNG#X|EefAjgf`{VEr zY_Bx0W85896!H`w3I`X14UYu}K}BMRgQLa+e>;o9A;K-e|3ClO3Xf60UU8Wj&I$+q O$x14|ED<;S{C@z&&0jVE literal 47123 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 NQ;<=8S`LBw{}=gmde;B| diff --git a/app/screenshots/sidetracks_settings.png b/app/screenshots/sidetracks_settings.png index b43d0f11ea6f0674f64f63669848ad432856a35f..8825bb8839a4bd5c2cc639bb1c36a8c33cefd207 100644 GIT binary patch literal 30754 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