Skip to content

Commit e5f1196

Browse files
feat: optimize android auto experience
1 parent 01aed95 commit e5f1196

3 files changed

Lines changed: 39 additions & 11 deletions

File tree

android/src/main/kotlin/project/pipepipe/app/service/MediaBrowserHelper.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import project.pipepipe.app.viewmodel.SearchViewModel
2020
import project.pipepipe.shared.infoitem.PlaylistInfo
2121
import project.pipepipe.shared.infoitem.StreamInfo
2222
import project.pipepipe.shared.infoitem.SupportedServiceInfo
23+
import kotlin.collections.take
2324

2425
/**
2526
* Helper class for Android Auto media browser functionality.
@@ -90,7 +91,7 @@ object MediaBrowserHelper {
9091
* Loads history items as MediaItems.
9192
*/
9293
suspend fun loadHistoryItems(): List<MediaItem> = withContext(Dispatchers.IO) {
93-
val historyItems = DatabaseOperations.loadStreamHistoryItems()
94+
val historyItems = DatabaseOperations.loadStreamHistoryItems().take(1000)
9495
historyItems.map { streamInfo ->
9596
createPlayableMediaItem(streamInfo)
9697
}
@@ -110,9 +111,9 @@ object MediaBrowserHelper {
110111
* Loads streams from a local playlist.
111112
*/
112113
suspend fun loadLocalPlaylistStreams(playlistId: Long): List<MediaItem> = withContext(Dispatchers.IO) {
113-
val streams = DatabaseOperations.loadPlaylistsItemsFromDatabase(playlistId)
114-
streams.map { streamInfo ->
115-
createPlayableMediaItem(streamInfo)
114+
val streams = DatabaseOperations.loadPlaylistsItemsFromDatabase(playlistId).take(1000)
115+
streams.mapIndexed { index, streamInfo ->
116+
createPlayableMediaItem(streamInfo, playlistId, index)
116117
}
117118
}
118119

@@ -127,7 +128,7 @@ object MediaBrowserHelper {
127128

128129
// Load remote playlist using ViewModel
129130
playlistVm.loadPlaylist(playlistInfo.url, playlistInfo.serviceId)
130-
val streams = playlistVm.uiState.value.list.itemList
131+
val streams = playlistVm.uiState.value.list.itemList.take(1000)
131132
streams.map { streamInfo ->
132133
createPlayableMediaItem(streamInfo)
133134
}
@@ -290,7 +291,7 @@ object MediaBrowserHelper {
290291
* Encodes all metadata in the mediaId since extras are lost during IPC.
291292
* Format: auto://{serviceId}/{realUrl}?name=...&artist=...&thumb=...&duration=...
292293
*/
293-
private fun createPlayableMediaItem(streamInfo: StreamInfo): MediaItem {
294+
private fun createPlayableMediaItem(streamInfo: StreamInfo, playlistId: Long? = null, index: Int? = null): MediaItem {
294295
val duration = (streamInfo.duration ?: 0) * 1000
295296

296297
// Build URL with query parameters to preserve metadata across IPC
@@ -303,6 +304,8 @@ object MediaBrowserHelper {
303304
streamInfo.uploaderName?.let { uriBuilder.appendQueryParameter("artist", it) }
304305
streamInfo.thumbnailUrl?.let { uriBuilder.appendQueryParameter("thumb", it) }
305306
if (duration > 0) uriBuilder.appendQueryParameter("duration", duration.toString())
307+
playlistId?.let { uriBuilder.appendQueryParameter("playlistId", it.toString()) }
308+
index?.let { uriBuilder.appendQueryParameter("index", it.toString()) }
306309

307310
val autoMediaId = uriBuilder.build().toString()
308311

android/src/main/kotlin/project/pipepipe/app/service/PlaybackService.kt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import project.pipepipe.app.database.DatabaseOperations
2525
import project.pipepipe.app.helper.ToastManager
2626
import project.pipepipe.app.helper.executeJobFlow
2727
import project.pipepipe.app.mediasource.CustomMediaSourceFactory
28+
import project.pipepipe.app.platform.getPlaybackStartPosition
2829
import project.pipepipe.app.platform.toMedia3MediaItem
2930
import project.pipepipe.app.platform.toPlatformMediaItem
3031
import project.pipepipe.app.platform.uuid
@@ -240,6 +241,7 @@ class PlaybackService : MediaLibraryService() {
240241
controller: MediaSession.ControllerInfo,
241242
mediaItems: MutableList<MediaItem>
242243
): ListenableFuture<List<MediaItem>> {
244+
// seems never called so don't need to sync queue. remove?
243245
return Futures.immediateFuture(mediaItems.toList())
244246
}
245247

@@ -250,12 +252,30 @@ class PlaybackService : MediaLibraryService() {
250252
startIndex: Int,
251253
startPositionMs: Long
252254
): ListenableFuture<MediaSession.MediaItemsWithStartPosition> {
253-
val result = MediaSession.MediaItemsWithStartPosition(
255+
if (mediaItems.size == 1) {
256+
val item = mediaItems.first()
257+
val playlistId = item.mediaId.substringAfter("playlistId=").substringBefore('&').toLongOrNull()
258+
val index = item.mediaId.substringAfter("index=").substringBefore('&').toIntOrNull()
259+
if (playlistId != null && index != null) {
260+
val items = runBlocking{ MediaBrowserHelper.loadLocalPlaylistStreams(playlistId) }
261+
GlobalScope.launch {
262+
SharedContext.queueManager.setQueue(items.map { it.toPlatformMediaItem() }, notifyOnly = true)
263+
}
264+
return Futures.immediateFuture(MediaSession.MediaItemsWithStartPosition(
265+
items,
266+
index,
267+
getPlaybackStartPosition(item.toPlatformMediaItem())
268+
))
269+
}
270+
}
271+
GlobalScope.launch {
272+
SharedContext.queueManager.setQueue(mediaItems.map { it.toPlatformMediaItem() }, notifyOnly = true)
273+
}
274+
return Futures.immediateFuture( MediaSession.MediaItemsWithStartPosition(
254275
mediaItems.toList(),
255276
startIndex,
256277
startPositionMs
257-
)
258-
return Futures.immediateFuture(result)
278+
))
259279
}
260280

261281
// Android Auto / Media Browser callbacks
@@ -309,6 +329,9 @@ class PlaybackService : MediaLibraryService() {
309329
return serviceScope.future {
310330
val historyItems = MediaBrowserHelper.loadHistoryItems()
311331
if (historyItems.isNotEmpty()) {
332+
GlobalScope.launch {
333+
SharedContext.queueManager.setQueue(historyItems.map { it.toPlatformMediaItem() }, notifyOnly = true)
334+
}
312335
MediaSession.MediaItemsWithStartPosition(
313336
historyItems,
314337
0,

library/src/commonMain/kotlin/project/pipepipe/app/QueueManager.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ class QueueManager {
2222

2323
fun isShuffled(): Boolean = backup != null
2424

25-
fun setQueue(items: List<PlatformMediaItem>, startIndex: Int = 0) {
25+
fun setQueue(items: List<PlatformMediaItem>, startIndex: Int = 0, notifyOnly: Boolean = false) {
2626
_queue.value = items.toList()
2727
backup = null
28-
SharedContext.platformMediaController!!.setQueue(items, startIndex)
28+
if (!notifyOnly) {
29+
SharedContext.platformMediaController!!.setQueue(items, startIndex)
30+
}
2931
}
3032

3133
fun addItem(item: PlatformMediaItem) {

0 commit comments

Comments
 (0)