mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-07-30 08:09:21 +00:00
Merge branch 'cmp-rewrite' of https://github.com/NuvioMedia/NuvioMobile into cmp-rewrite
This commit is contained in:
commit
8a1efa2551
3 changed files with 98 additions and 55 deletions
|
|
@ -2325,8 +2325,10 @@ private fun MainAppContent(
|
|||
)
|
||||
|
||||
if (!forceInternal && (forceExternal || playerSettings.externalPlayerEnabled)) {
|
||||
coroutineScope.launch { openExternalPlayback(playerLaunch) }
|
||||
StreamsRepository.cancelLoading()
|
||||
streamRouteScope.launch {
|
||||
openExternalPlayback(playerLaunch)
|
||||
StreamsRepository.cancelLoading()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import kotlinx.serialization.json.addJsonObject
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
|
|
@ -33,35 +34,47 @@ suspend fun prepareExternalPlayerLaunch(
|
|||
preferredLanguage: String,
|
||||
secondaryLanguage: String?,
|
||||
onOverlayMessage: (String?) -> Unit,
|
||||
): ExternalPlayerPlaybackRequest {
|
||||
): ExternalPlayerPlaybackRequest = coroutineScope {
|
||||
var result = request
|
||||
|
||||
if (forwardSubtitles && !preferredLanguage.equals(SubtitleLanguageOption.NONE, ignoreCase = true)) {
|
||||
onOverlayMessage("Loading subtitles from addons...")
|
||||
val subtitlesDeferred = if (forwardSubtitles && !preferredLanguage.equals(SubtitleLanguageOption.NONE, ignoreCase = true)) {
|
||||
async {
|
||||
onOverlayMessage("Loading subtitles from addons...")
|
||||
|
||||
val subtitles = SubtitleForwarder.fetchForExternalPlayer(
|
||||
type = type,
|
||||
videoId = videoId,
|
||||
preferredLanguage = preferredLanguage,
|
||||
secondaryLanguage = secondaryLanguage,
|
||||
)
|
||||
val subtitles = SubtitleForwarder.fetchForExternalPlayer(
|
||||
type = type,
|
||||
videoId = videoId,
|
||||
preferredLanguage = preferredLanguage,
|
||||
secondaryLanguage = secondaryLanguage,
|
||||
)
|
||||
|
||||
if (subtitles != null) {
|
||||
onOverlayMessage("Downloading subtitles...")
|
||||
val cachedSubtitles = SubtitleCacheProvider.cacheForExternalPlayer(subtitles)
|
||||
// Fallback: use original URLs if caching fails
|
||||
result = result.copy(subtitles = cachedSubtitles ?: subtitles)
|
||||
if (subtitles != null) {
|
||||
onOverlayMessage("Downloading subtitles...")
|
||||
val cachedSubtitles = SubtitleCacheProvider.cacheForExternalPlayer(subtitles)
|
||||
// Fallback: use original URLs if caching fails
|
||||
cachedSubtitles ?: subtitles
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (sendSkipSegments) {
|
||||
val skipSegmentsJson = resolveSkipSegmentsJson(videoId, request.season, request.episode)
|
||||
if (skipSegmentsJson != null) {
|
||||
result = result.copy(skipSegmentsJson = skipSegmentsJson)
|
||||
}
|
||||
val skipSegmentsDeferred = if (sendSkipSegments) {
|
||||
async { resolveSkipSegmentsJson(videoId, request.season, request.episode) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return result
|
||||
subtitlesDeferred?.await()?.let { subtitles ->
|
||||
result = result.copy(subtitles = subtitles)
|
||||
}
|
||||
skipSegmentsDeferred?.await()?.let { skipSegmentsJson ->
|
||||
result = result.copy(skipSegmentsJson = skipSegmentsJson)
|
||||
}
|
||||
|
||||
return@coroutineScope result
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.nuvio.app.features.player.skip
|
||||
|
||||
import com.nuvio.app.features.player.PlayerSettingsRepository
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
|
||||
object SkipIntroRepository {
|
||||
|
||||
|
|
@ -17,48 +19,64 @@ object SkipIntroRepository {
|
|||
season: Int,
|
||||
episode: Int,
|
||||
requireSkipIntroEnabled: Boolean = true,
|
||||
): List<SkipInterval> {
|
||||
if (imdbId == null) return emptyList()
|
||||
): List<SkipInterval> = coroutineScope {
|
||||
if (imdbId == null) return@coroutineScope emptyList()
|
||||
val settings = PlayerSettingsRepository.uiState.value
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return emptyList()
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return@coroutineScope emptyList()
|
||||
|
||||
val cacheKey = "$imdbId:$season:$episode"
|
||||
cache[cacheKey]?.let { return it }
|
||||
cache[cacheKey]?.let { return@coroutineScope it }
|
||||
|
||||
val introDb = if (introDbConfigured) fetchFromIntroDb(imdbId, season, episode) else emptyList()
|
||||
val entries = resolveImdbEntries(imdbId)
|
||||
val animeSkip = fetchAnimeSkipForEntries(entries, season, episode)
|
||||
val introDbDeferred = async {
|
||||
if (introDbConfigured) fetchFromIntroDb(imdbId, season, episode) else emptyList()
|
||||
}
|
||||
val entriesDeferred = async { resolveImdbEntries(imdbId) }
|
||||
val entries = entriesDeferred.await()
|
||||
val animeSkipDeferred = async { fetchAnimeSkipForEntries(entries, season, episode) }
|
||||
val malId = entries.getOrNull(season - 1)?.myanimelist?.toString()
|
||||
?: entries.firstOrNull()?.myanimelist?.toString()
|
||||
val aniSkip = if (malId != null) fetchFromAniSkip(malId, episode) else emptyList()
|
||||
val aniSkipDeferred = async {
|
||||
if (malId != null) fetchFromAniSkip(malId, episode) else emptyList()
|
||||
}
|
||||
|
||||
return mergeByPriority(introDb, animeSkip, aniSkip).also { cache[cacheKey] = it }
|
||||
return@coroutineScope mergeByPriority(
|
||||
introDbDeferred.await(),
|
||||
animeSkipDeferred.await(),
|
||||
aniSkipDeferred.await(),
|
||||
).also { cache[cacheKey] = it }
|
||||
}
|
||||
|
||||
suspend fun getSkipIntervalsForMal(
|
||||
malId: String,
|
||||
episode: Int,
|
||||
requireSkipIntroEnabled: Boolean = true,
|
||||
): List<SkipInterval> {
|
||||
): List<SkipInterval> = coroutineScope {
|
||||
val settings = PlayerSettingsRepository.uiState.value
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return emptyList()
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return@coroutineScope emptyList()
|
||||
|
||||
val cacheKey = "mal:$malId:$episode"
|
||||
cache[cacheKey]?.let { return it }
|
||||
cache[cacheKey]?.let { return@coroutineScope it }
|
||||
|
||||
val aniSkip = fetchFromAniSkip(malId, episode)
|
||||
val aniSkipDeferred = async { fetchFromAniSkip(malId, episode) }
|
||||
|
||||
val imdbId = try {
|
||||
SkipIntroApi.resolveMalToImdb(malId)?.imdb
|
||||
} catch (_: Exception) { null }
|
||||
val imdbIdDeferred = async {
|
||||
try {
|
||||
SkipIntroApi.resolveMalToImdb(malId)?.imdb
|
||||
} catch (_: Exception) { null }
|
||||
}
|
||||
|
||||
var introDb = emptyList<SkipInterval>()
|
||||
var animeSkip = emptyList<SkipInterval>()
|
||||
val imdbId = imdbIdDeferred.await()
|
||||
if (imdbId != null) {
|
||||
val entries = resolveImdbEntries(imdbId)
|
||||
val season = entries.indexOfFirst { it.myanimelist == malId.toIntOrNull() } + 1
|
||||
if (introDbConfigured) introDb = fetchFromIntroDb(imdbId, season, episode)
|
||||
animeSkip = fetchAnimeSkipForEntries(entries, season, episode)
|
||||
val introDbDeferred = async {
|
||||
if (introDbConfigured) fetchFromIntroDb(imdbId, season, episode) else emptyList()
|
||||
}
|
||||
val animeSkipDeferred = async { fetchAnimeSkipForEntries(entries, season, episode) }
|
||||
introDb = introDbDeferred.await()
|
||||
animeSkip = animeSkipDeferred.await()
|
||||
} else {
|
||||
val anilistId = try {
|
||||
SkipIntroApi.resolveMalToAnilist(malId)?.anilist?.toString()
|
||||
|
|
@ -66,36 +84,46 @@ object SkipIntroRepository {
|
|||
if (anilistId != null) animeSkip = fetchFromAnimeSkip(anilistId, episode, season = null)
|
||||
}
|
||||
|
||||
return mergeByPriority(introDb, animeSkip, aniSkip).also { cache[cacheKey] = it }
|
||||
return@coroutineScope mergeByPriority(introDb, animeSkip, aniSkipDeferred.await()).also { cache[cacheKey] = it }
|
||||
}
|
||||
|
||||
suspend fun getSkipIntervalsForKitsu(
|
||||
kitsuId: String,
|
||||
episode: Int,
|
||||
requireSkipIntroEnabled: Boolean = true,
|
||||
): List<SkipInterval> {
|
||||
): List<SkipInterval> = coroutineScope {
|
||||
val settings = PlayerSettingsRepository.uiState.value
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return emptyList()
|
||||
if (requireSkipIntroEnabled && !settings.skipIntroEnabled) return@coroutineScope emptyList()
|
||||
|
||||
val cacheKey = "kitsu:$kitsuId:$episode"
|
||||
cache[cacheKey]?.let { return it }
|
||||
cache[cacheKey]?.let { return@coroutineScope it }
|
||||
|
||||
val malId = try {
|
||||
SkipIntroApi.resolveKitsuToMal(kitsuId)?.myanimelist?.toString()
|
||||
} catch (_: Exception) { null }
|
||||
val aniSkip = if (malId != null) fetchFromAniSkip(malId, episode) else emptyList()
|
||||
|
||||
val imdbId = try {
|
||||
SkipIntroApi.resolveKitsuToImdb(kitsuId)?.imdb
|
||||
} catch (_: Exception) { null }
|
||||
val malIdDeferred = async {
|
||||
try {
|
||||
SkipIntroApi.resolveKitsuToMal(kitsuId)?.myanimelist?.toString()
|
||||
} catch (_: Exception) { null }
|
||||
}
|
||||
val imdbIdDeferred = async {
|
||||
try {
|
||||
SkipIntroApi.resolveKitsuToImdb(kitsuId)?.imdb
|
||||
} catch (_: Exception) { null }
|
||||
}
|
||||
val aniSkipDeferred = async {
|
||||
malIdDeferred.await()?.let { fetchFromAniSkip(it, episode) } ?: emptyList()
|
||||
}
|
||||
|
||||
var introDb = emptyList<SkipInterval>()
|
||||
var animeSkip = emptyList<SkipInterval>()
|
||||
val imdbId = imdbIdDeferred.await()
|
||||
if (imdbId != null) {
|
||||
val entries = resolveImdbEntries(imdbId)
|
||||
val season = entries.indexOfFirst { it.kitsu == kitsuId.toIntOrNull() } + 1
|
||||
if (introDbConfigured) introDb = fetchFromIntroDb(imdbId, season, episode)
|
||||
animeSkip = fetchAnimeSkipForEntries(entries, season, episode)
|
||||
val introDbDeferred = async {
|
||||
if (introDbConfigured) fetchFromIntroDb(imdbId, season, episode) else emptyList()
|
||||
}
|
||||
val animeSkipDeferred = async { fetchAnimeSkipForEntries(entries, season, episode) }
|
||||
introDb = introDbDeferred.await()
|
||||
animeSkip = animeSkipDeferred.await()
|
||||
} else {
|
||||
val anilistId = try {
|
||||
SkipIntroApi.resolveKitsuToAnilist(kitsuId)?.anilist?.toString()
|
||||
|
|
@ -103,7 +131,7 @@ object SkipIntroRepository {
|
|||
if (anilistId != null) animeSkip = fetchFromAnimeSkip(anilistId, episode, season = null)
|
||||
}
|
||||
|
||||
return mergeByPriority(introDb, animeSkip, aniSkip).also { cache[cacheKey] = it }
|
||||
return@coroutineScope mergeByPriority(introDb, animeSkip, aniSkipDeferred.await()).also { cache[cacheKey] = it }
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue