mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Fix crash when go backing too quickly (#309)
Fixes #306 Also fixes network-on-main and potential concurrency issues
This commit is contained in:
parent
78e7eb421f
commit
5fbe12e6cf
3 changed files with 54 additions and 42 deletions
|
|
@ -5,12 +5,17 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
|
||||
import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import com.google.common.cache.CacheLoader
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.exception.InvalidStatusException
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
|
|
@ -28,46 +33,47 @@ class DatePlayedService
|
|||
@Inject
|
||||
constructor(
|
||||
private val api: ApiClient,
|
||||
@param:IoCoroutineScope private val scope: CoroutineScope,
|
||||
) {
|
||||
private val datePlayedCache =
|
||||
CacheBuilder
|
||||
.newBuilder()
|
||||
.maximumSize(AppPreference.HomePageItems.max)
|
||||
.expireAfterWrite(2, TimeUnit.HOURS)
|
||||
.build<SeriesItemId, LocalDateTime?>(
|
||||
object :
|
||||
CacheLoader<SeriesItemId, LocalDateTime>() {
|
||||
override fun load(key: SeriesItemId): LocalDateTime = getLastPlayed(key)
|
||||
.build<SeriesItemId, Deferred<LocalDateTime>>(
|
||||
object : CacheLoader<SeriesItemId, Deferred<LocalDateTime>>() {
|
||||
override fun load(key: SeriesItemId): Deferred<LocalDateTime> = getLastPlayed(key)
|
||||
},
|
||||
)
|
||||
|
||||
private fun getLastPlayed(key: SeriesItemId): LocalDateTime {
|
||||
private fun getLastPlayed(key: SeriesItemId): Deferred<LocalDateTime> {
|
||||
val request =
|
||||
GetEpisodesRequest(
|
||||
seriesId = key.seriesId,
|
||||
adjacentTo = key.itemId,
|
||||
limit = 1,
|
||||
)
|
||||
return try {
|
||||
return scope.async(Dispatchers.IO) {
|
||||
try {
|
||||
val result =
|
||||
runBlocking {
|
||||
GetEpisodesRequestHandler
|
||||
.execute(
|
||||
api,
|
||||
request,
|
||||
).content.items
|
||||
}
|
||||
result.firstOrNull()?.userData?.lastPlayedDate ?: LocalDateTime.MIN
|
||||
} catch (ex: InvalidStatusException) {
|
||||
Timber.w("Error fetching %s: %s", key, ex.localizedMessage)
|
||||
LocalDateTime.MIN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getLastPlayed(item: BaseItem): LocalDateTime? {
|
||||
suspend fun getLastPlayed(item: BaseItem): LocalDateTime? =
|
||||
withContext(Dispatchers.IO) {
|
||||
val seriesId = item.data.seriesId
|
||||
return if (seriesId != null) {
|
||||
datePlayedCache.get(SeriesItemId(seriesId, item.id))
|
||||
return@withContext if (seriesId != null) {
|
||||
datePlayedCache.get(SeriesItemId(seriesId, item.id)).await()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,11 @@ class NavigationManager
|
|||
* Go to the previous page
|
||||
*/
|
||||
fun goBack() {
|
||||
synchronized(this) {
|
||||
if (backStack.size > 1) {
|
||||
backStack.removeLastOrNull()
|
||||
}
|
||||
}
|
||||
log()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,24 +264,26 @@ class HomeViewModel
|
|||
private suspend fun buildCombined(
|
||||
resume: List<BaseItem>,
|
||||
nextUp: List<BaseItem>,
|
||||
): 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 ->
|
||||
semaphore.withPermit {
|
||||
viewModelScope.async {
|
||||
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)
|
||||
|
|
@ -289,7 +291,7 @@ class HomeViewModel
|
|||
val result = (resume + nextUp).sortedByDescending { timestamps[it.id] }
|
||||
val duration = (System.currentTimeMillis() - start).milliseconds
|
||||
Timber.v("buildCombined took %s", duration)
|
||||
return result
|
||||
return@withContext result
|
||||
}
|
||||
|
||||
fun setWatched(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue