mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Add multi-part videos to playback queue (#520)
## Description When playing media with multiple parts, the additional parts will be added to the playback queue. This means a multi-part movie or episode will be play all of the parts in order before the next item in the queue. This only affects the playback queue. There's no UI changes and no indication that a movie or episode has multiple parts. I'll work on that in a separate PR. ### Related issues Related to #455
This commit is contained in:
parent
6df89fe8c9
commit
925a4b9d82
3 changed files with 68 additions and 20 deletions
|
|
@ -10,6 +10,7 @@ import com.github.damontecres.wholphin.data.model.PlaylistInfo
|
|||
import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.wholphin.ui.components.baseItemKinds
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.gt
|
||||
import com.github.damontecres.wholphin.ui.indexOfFirstOrNull
|
||||
import com.github.damontecres.wholphin.ui.playback.playable
|
||||
import com.github.damontecres.wholphin.ui.toServerString
|
||||
|
|
@ -22,6 +23,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
|||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.playlistsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.videosApi
|
||||
import org.jellyfin.sdk.model.api.BaseItemDto
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.CreatePlaylistDto
|
||||
|
|
@ -64,10 +66,14 @@ class PlaylistCreator
|
|||
sortBy = if (shuffled) ItemSortBy.RANDOM else null,
|
||||
limit = Playlist.MAX_SIZE,
|
||||
)
|
||||
val episodes = GetEpisodesRequestHandler.execute(api, request).content.items
|
||||
val episodes =
|
||||
GetEpisodesRequestHandler
|
||||
.execute(api, request)
|
||||
.content.items
|
||||
.convertAndAddParts(false)
|
||||
val startIndex =
|
||||
episodeId?.let { episodes.indexOfFirstOrNull { it.id == episodeId } } ?: 0
|
||||
return Playlist(episodes.map { BaseItem.from(it, api) }, startIndex)
|
||||
return Playlist(episodes, startIndex)
|
||||
}
|
||||
|
||||
suspend fun createFromPlaylistId(
|
||||
|
|
@ -82,12 +88,11 @@ class PlaylistCreator
|
|||
startIndex = startIndex,
|
||||
limit = Playlist.MAX_SIZE,
|
||||
)
|
||||
val items = GetPlaylistItemsRequestHandler.execute(api, request).content.items
|
||||
var baseItems = items.map { BaseItem.from(it, api) }
|
||||
var items = GetPlaylistItemsRequestHandler.execute(api, request).content.items
|
||||
if (shuffled) {
|
||||
baseItems = baseItems.shuffled()
|
||||
items = items.shuffled()
|
||||
}
|
||||
return Playlist(baseItems, 0)
|
||||
return Playlist(items.convertAndAddParts(), 0)
|
||||
}
|
||||
|
||||
private suspend fun createFromCollection(
|
||||
|
|
@ -120,19 +125,20 @@ class PlaylistCreator
|
|||
),
|
||||
)
|
||||
val items =
|
||||
GetItemsRequestHandler.execute(api, request).content.items.map {
|
||||
BaseItem.from(it, api)
|
||||
}
|
||||
GetItemsRequestHandler
|
||||
.execute(api, request)
|
||||
.content.items
|
||||
.convertAndAddParts()
|
||||
return Playlist(items, 0)
|
||||
}
|
||||
|
||||
suspend fun createFrom(
|
||||
item: BaseItemDto,
|
||||
startIndex: Int = 0,
|
||||
sortAndDirection: SortAndDirection?,
|
||||
shuffled: Boolean,
|
||||
recursive: Boolean,
|
||||
filter: GetItemsFilter,
|
||||
sortAndDirection: SortAndDirection? = SortAndDirection.DEFAULT,
|
||||
shuffled: Boolean = false,
|
||||
recursive: Boolean = false,
|
||||
filter: GetItemsFilter = GetItemsFilter(),
|
||||
): PlaylistCreationResult =
|
||||
when (item.type) {
|
||||
BaseItemKind.BOX_SET,
|
||||
|
|
@ -166,7 +172,7 @@ class PlaylistCreator
|
|||
),
|
||||
)
|
||||
} else {
|
||||
PlaylistCreationResult.Error(null, "Episode has not seriesId")
|
||||
PlaylistCreationResult.Error(null, "Episode has no seriesId")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +188,7 @@ class PlaylistCreator
|
|||
),
|
||||
)
|
||||
} else {
|
||||
PlaylistCreationResult.Error(null, "Episode has not seriesId")
|
||||
PlaylistCreationResult.Error(null, "Episode has no seriesId")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +213,26 @@ class PlaylistCreator
|
|||
)
|
||||
}
|
||||
|
||||
BaseItemKind.MOVIE,
|
||||
BaseItemKind.VIDEO,
|
||||
BaseItemKind.MUSIC_VIDEO,
|
||||
-> {
|
||||
val list =
|
||||
buildList {
|
||||
add(BaseItem(item, false))
|
||||
|
||||
if (item.partCount.gt(1)) {
|
||||
api.videosApi
|
||||
.getAdditionalPart(item.id)
|
||||
.content.items
|
||||
.map {
|
||||
BaseItem(it, false)
|
||||
}.let(::addAll)
|
||||
}
|
||||
}
|
||||
PlaylistCreationResult.Success(Playlist(list, 0))
|
||||
}
|
||||
|
||||
// Not support yet
|
||||
// BaseItemKind.AGGREGATE_FOLDER -> TODO()
|
||||
// BaseItemKind.FOLDER -> TODO()
|
||||
|
|
@ -220,6 +246,20 @@ class PlaylistCreator
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun List<BaseItemDto>.convertAndAddParts(useSeriesForPrimary: Boolean = false): List<BaseItem> =
|
||||
buildList {
|
||||
this@convertAndAddParts.forEach { ep ->
|
||||
add(BaseItem(ep, useSeriesForPrimary))
|
||||
if (ep.partCount.gt(1)) {
|
||||
val parts =
|
||||
api.videosApi.getAdditionalPart(ep.id).content.items.map { part ->
|
||||
BaseItem(part, useSeriesForPrimary)
|
||||
}
|
||||
addAll(parts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getServerPlaylists(
|
||||
mediaType: MediaType?,
|
||||
scope: CoroutineScope,
|
||||
|
|
|
|||
|
|
@ -418,3 +418,10 @@ fun rememberBackDropImage(item: BaseItem): String? {
|
|||
val imageUrlService = LocalImageUrlService.current
|
||||
return remember(item) { imageUrlService.getItemImageUrl(item, ImageType.BACKDROP) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this, coalescing nulls to zero, is greater than that
|
||||
*/
|
||||
fun Int?.gt(that: Int) = (this ?: 0) > that
|
||||
|
||||
fun Int?.lt(that: Int) = (this ?: 0) < that
|
||||
|
|
|
|||
|
|
@ -286,11 +286,12 @@ class PlaybackViewModel
|
|||
playNextUp()
|
||||
}
|
||||
|
||||
if (!isPlaylist && queriedItem.type == BaseItemKind.EPISODE) {
|
||||
val playlist =
|
||||
playlistCreator.createFromEpisode(queriedItem.seriesId!!, queriedItem.id)
|
||||
if (!isPlaylist) {
|
||||
val result = playlistCreator.createFrom(queriedItem)
|
||||
if (result is PlaylistCreationResult.Success && result.playlist.items.isNotEmpty()) {
|
||||
withContext(Dispatchers.Main) {
|
||||
this@PlaybackViewModel.playlist.value = playlist
|
||||
this@PlaybackViewModel.playlist.value = result.playlist
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue