mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Apply combined continue/next up, if enabled, on library recommended tabs (#533)
## Description Respect the combine Continue Watching & Next Up setting on TV Show library recommended tabs. This uses the same implementation on the home screen, but only includes items for that particular tv show library. ### Related issues Fixes #532
This commit is contained in:
parent
11dda19b8a
commit
6df89fe8c9
2 changed files with 96 additions and 59 deletions
|
|
@ -9,14 +9,15 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
|
import com.github.damontecres.wholphin.services.DatePlayedService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
|
import com.github.damontecres.wholphin.ui.main.buildCombinedNextUp
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
import com.github.damontecres.wholphin.ui.toBaseItems
|
import com.github.damontecres.wholphin.ui.toBaseItems
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
|
@ -57,6 +58,7 @@ class RecommendedTvShowViewModel
|
||||||
private val api: ApiClient,
|
private val api: ApiClient,
|
||||||
private val serverRepository: ServerRepository,
|
private val serverRepository: ServerRepository,
|
||||||
private val preferencesDataStore: DataStore<AppPreferences>,
|
private val preferencesDataStore: DataStore<AppPreferences>,
|
||||||
|
private val datePlayedService: DatePlayedService,
|
||||||
@Assisted val parentId: UUID,
|
@Assisted val parentId: UUID,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
favoriteWatchManager: FavoriteWatchManager,
|
favoriteWatchManager: FavoriteWatchManager,
|
||||||
|
|
@ -78,17 +80,17 @@ class RecommendedTvShowViewModel
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
|
viewModelScope.launch(Dispatchers.IO + ExceptionHandler()) {
|
||||||
val itemsPerRow =
|
val preferences =
|
||||||
preferencesDataStore.data
|
preferencesDataStore.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
||||||
.firstOrNull()
|
val combineNextUp = preferences.homePagePreferences.combineContinueNext
|
||||||
?.homePagePreferences
|
val itemsPerRow = preferences.homePagePreferences.maxItemsPerRow
|
||||||
?.maxItemsPerRow
|
val userId = serverRepository.currentUser.value?.id
|
||||||
?: AppPreference.HomePageItems.defaultValue.toInt()
|
|
||||||
try {
|
try {
|
||||||
val resumeItemsDeferred =
|
val resumeItemsDeferred =
|
||||||
viewModelScope.async(Dispatchers.IO) {
|
viewModelScope.async(Dispatchers.IO) {
|
||||||
val resumeItemsRequest =
|
val resumeItemsRequest =
|
||||||
GetResumeItemsRequest(
|
GetResumeItemsRequest(
|
||||||
|
userId = userId,
|
||||||
parentId = parentId,
|
parentId = parentId,
|
||||||
fields = SlimItemFields,
|
fields = SlimItemFields,
|
||||||
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
includeItemTypes = listOf(BaseItemKind.EPISODE),
|
||||||
|
|
@ -106,12 +108,14 @@ class RecommendedTvShowViewModel
|
||||||
viewModelScope.async(Dispatchers.IO) {
|
viewModelScope.async(Dispatchers.IO) {
|
||||||
val nextUpRequest =
|
val nextUpRequest =
|
||||||
GetNextUpRequest(
|
GetNextUpRequest(
|
||||||
parentId = parentId,
|
userId = userId,
|
||||||
fields = SlimItemFields,
|
fields = SlimItemFields,
|
||||||
enableUserData = true,
|
imageTypeLimit = 1,
|
||||||
startIndex = 0,
|
parentId = parentId,
|
||||||
limit = itemsPerRow,
|
limit = itemsPerRow,
|
||||||
enableTotalRecordCount = false,
|
enableResumable = false,
|
||||||
|
enableUserData = true,
|
||||||
|
enableRewatching = preferences.homePagePreferences.enableRewatchingNextUp,
|
||||||
)
|
)
|
||||||
|
|
||||||
GetNextUpRequestHandler
|
GetNextUpRequestHandler
|
||||||
|
|
@ -119,21 +123,45 @@ class RecommendedTvShowViewModel
|
||||||
.toBaseItems(api, true)
|
.toBaseItems(api, true)
|
||||||
}
|
}
|
||||||
val resumeItems = resumeItemsDeferred.await()
|
val resumeItems = resumeItemsDeferred.await()
|
||||||
update(
|
|
||||||
R.string.continue_watching,
|
|
||||||
HomeRowLoadingState.Success(
|
|
||||||
context.getString(R.string.continue_watching),
|
|
||||||
resumeItems,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val nextUpItems = nextUpItemsDeferred.await()
|
val nextUpItems = nextUpItemsDeferred.await()
|
||||||
update(
|
if (combineNextUp) {
|
||||||
R.string.next_up,
|
val combined =
|
||||||
HomeRowLoadingState.Success(
|
buildCombinedNextUp(
|
||||||
context.getString(R.string.next_up),
|
viewModelScope,
|
||||||
nextUpItems,
|
datePlayedService,
|
||||||
),
|
resumeItems,
|
||||||
)
|
nextUpItems,
|
||||||
|
)
|
||||||
|
update(
|
||||||
|
R.string.continue_watching,
|
||||||
|
HomeRowLoadingState.Success(
|
||||||
|
context.getString(R.string.continue_watching),
|
||||||
|
combined,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
update(
|
||||||
|
R.string.next_up,
|
||||||
|
HomeRowLoadingState.Success(
|
||||||
|
context.getString(R.string.next_up),
|
||||||
|
listOf(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
update(
|
||||||
|
R.string.continue_watching,
|
||||||
|
HomeRowLoadingState.Success(
|
||||||
|
context.getString(R.string.continue_watching),
|
||||||
|
resumeItems,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
update(
|
||||||
|
R.string.next_up,
|
||||||
|
HomeRowLoadingState.Success(
|
||||||
|
context.getString(R.string.next_up),
|
||||||
|
nextUpItems,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (resumeItems.isNotEmpty() || nextUpItems.isNotEmpty()) {
|
if (resumeItems.isNotEmpty() || nextUpItems.isNotEmpty()) {
|
||||||
loading.setValueOnMain(LoadingState.Success)
|
loading.setValueOnMain(LoadingState.Success)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import com.github.damontecres.wholphin.util.LoadingState
|
||||||
import com.github.damontecres.wholphin.util.supportItemKinds
|
import com.github.damontecres.wholphin.util.supportItemKinds
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
|
|
@ -109,7 +110,13 @@ class HomeViewModel
|
||||||
val watching =
|
val watching =
|
||||||
buildList {
|
buildList {
|
||||||
if (prefs.combineContinueNext) {
|
if (prefs.combineContinueNext) {
|
||||||
val items = buildCombined(resume, nextUp)
|
val items =
|
||||||
|
buildCombinedNextUp(
|
||||||
|
viewModelScope,
|
||||||
|
datePlayedService,
|
||||||
|
resume,
|
||||||
|
nextUp,
|
||||||
|
)
|
||||||
add(
|
add(
|
||||||
HomeRowLoadingState.Success(
|
HomeRowLoadingState.Success(
|
||||||
title = context.getString(R.string.continue_watching),
|
title = context.getString(R.string.continue_watching),
|
||||||
|
|
@ -267,39 +274,6 @@ class HomeViewModel
|
||||||
latestRows.setValueOnMain(rows)
|
latestRows.setValueOnMain(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun buildCombined(
|
|
||||||
resume: List<BaseItem>,
|
|
||||||
nextUp: List<BaseItem>,
|
|
||||||
): List<BaseItem> =
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val start = System.currentTimeMillis()
|
|
||||||
val semaphore = Semaphore(3)
|
|
||||||
val deferred =
|
|
||||||
nextUp
|
|
||||||
.filter { it.data.seriesId != null }
|
|
||||||
.map { item ->
|
|
||||||
viewModelScope.async(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
semaphore.withPermit {
|
|
||||||
datePlayedService.getLastPlayed(item)
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
Timber.e(ex, "Error fetching %s", item.id)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val nextUpLastPlayed = deferred.awaitAll()
|
|
||||||
val timestamps = mutableMapOf<UUID, LocalDateTime?>()
|
|
||||||
nextUp.map { it.id }.zip(nextUpLastPlayed).toMap(timestamps)
|
|
||||||
resume.forEach { timestamps[it.id] = it.data.userData?.lastPlayedDate }
|
|
||||||
val result = (resume + nextUp).sortedByDescending { timestamps[it.id] }
|
|
||||||
val duration = (System.currentTimeMillis() - start).milliseconds
|
|
||||||
Timber.v("buildCombined took %s", duration)
|
|
||||||
return@withContext result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setWatched(
|
fun setWatched(
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
played: Boolean,
|
played: Boolean,
|
||||||
|
|
@ -340,3 +314,38 @@ data class LatestData(
|
||||||
val title: String,
|
val title: String,
|
||||||
val request: GetLatestMediaRequest,
|
val request: GetLatestMediaRequest,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
suspend fun buildCombinedNextUp(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
datePlayedService: DatePlayedService,
|
||||||
|
resume: List<BaseItem>,
|
||||||
|
nextUp: List<BaseItem>,
|
||||||
|
): List<BaseItem> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
val semaphore = Semaphore(3)
|
||||||
|
val deferred =
|
||||||
|
nextUp
|
||||||
|
.filter { it.data.seriesId != null }
|
||||||
|
.map { item ->
|
||||||
|
scope.async(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
semaphore.withPermit {
|
||||||
|
datePlayedService.getLastPlayed(item)
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error fetching %s", item.id)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextUpLastPlayed = deferred.awaitAll()
|
||||||
|
val timestamps = mutableMapOf<UUID, LocalDateTime?>()
|
||||||
|
nextUp.map { it.id }.zip(nextUpLastPlayed).toMap(timestamps)
|
||||||
|
resume.forEach { timestamps[it.id] = it.data.userData?.lastPlayedDate }
|
||||||
|
val result = (resume + nextUp).sortedByDescending { timestamps[it.id] }
|
||||||
|
val duration = (System.currentTimeMillis() - start).milliseconds
|
||||||
|
Timber.v("buildCombined took %s", duration)
|
||||||
|
return@withContext result
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue