feat: save resolved links in memory

This commit is contained in:
tapframe 2026-05-22 17:11:34 +05:30
parent 2e35652e81
commit e85ea4f322
2 changed files with 119 additions and 3 deletions

View file

@ -64,6 +64,13 @@ internal class CloudLibraryStore(
?: return CloudLibraryPlaybackResult.MissingCredentials
val api = providerApis.firstOrNull { it.provider.id == item.providerId }
?: return CloudLibraryPlaybackResult.Failed()
file.playbackUrl?.takeIf { it.isNotBlank() }?.let { url ->
return CloudLibraryPlaybackResult.Success(
url = url,
filename = file.name.takeIf { it.isNotBlank() },
videoSizeBytes = file.sizeBytes,
)
}
return api.resolvePlayback(
apiKey = credential.apiKey,
item = item,
@ -186,7 +193,26 @@ object CloudLibraryRepository {
if (!DebridSettingsRepository.snapshot().cloudLibraryEnabled) {
return CloudLibraryPlaybackResult.Failed("Cloud library is disabled.")
}
return store.resolvePlayback(item, file)
val result = store.resolvePlayback(item, file)
if (result is CloudLibraryPlaybackResult.Success) {
rememberResolvedPlaybackUrl(item = item, file = file, url = result.url)
}
return result
}
private fun rememberResolvedPlaybackUrl(
item: CloudLibraryItem,
file: CloudLibraryFile,
url: String,
) {
if (url.isBlank()) return
_uiState.update { current ->
current.withResolvedPlaybackUrl(
item = item,
file = file,
url = url,
)
}
}
private fun connectedCloudCredentials(): List<DebridServiceCredential> =
@ -248,3 +274,35 @@ internal fun CloudLibraryUiState.findPlaybackTargetForProgress(
val singleFile = singleItem.playableFiles.singleOrNull() ?: return null
return CloudLibraryPlaybackTarget(item = singleItem, file = singleFile)
}
internal fun CloudLibraryUiState.withResolvedPlaybackUrl(
item: CloudLibraryItem,
file: CloudLibraryFile,
url: String,
): CloudLibraryUiState {
val normalizedUrl = url.trim().takeIf { it.isNotBlank() } ?: return this
val targetItemKey = item.stableKey
val targetFileKey = file.stableKey
var didUpdate = false
val updatedProviders = providers.map { providerState ->
if (providerState.providerId != item.providerId) return@map providerState
val updatedItems = providerState.items.map { candidateItem ->
if (candidateItem.stableKey != targetItemKey) return@map candidateItem
val updatedFiles = candidateItem.files.map { candidateFile ->
if (candidateFile.stableKey != targetFileKey) {
candidateFile
} else {
didUpdate = true
candidateFile.copy(playbackUrl = normalizedUrl)
}
}
candidateItem.copy(files = updatedFiles)
}
providerState.copy(items = updatedItems)
}
return if (didUpdate) {
copy(providers = updatedProviders)
} else {
this
}
}

View file

@ -130,6 +130,59 @@ class CloudLibraryStoreTest {
assertEquals(item.playableFiles.single(), target.file)
}
@Test
fun `resolve playback reuses already resolved file url`() = runBlocking {
val provider = cloudProvider(id = "premiumize", name = "Premiumize")
val api = FakeCloudProviderApi(
provider = provider,
items = emptyList(),
)
val store = CloudLibraryStore(
credentialsProvider = {
listOf(DebridServiceCredential(provider, "token"))
},
providerApis = listOf(api),
)
val item = cloudItem(provider, "ready")
val file = item.playableFiles.single().copy(playbackUrl = "https://cached.example/video.mkv")
val result = store.resolvePlayback(item = item, file = file)
assertTrue(result is CloudLibraryPlaybackResult.Success)
assertEquals("https://cached.example/video.mkv", result.url)
assertEquals(0, api.resolvePlaybackCalls)
}
@Test
fun `resolved playback url is remembered in cloud library state`() {
val provider = cloudProvider(id = "torbox", name = "TorBox")
val item = cloudItem(provider, "29773238")
val file = item.playableFiles.single()
val state = CloudLibraryUiState(
isLoaded = true,
providers = listOf(
CloudLibraryProviderState(
provider = provider,
items = listOf(item),
),
),
)
val updated = state.withResolvedPlaybackUrl(
item = item,
file = file,
url = "https://resolved.example/movie.mkv",
)
val target = assertNotNull(
updated.findPlaybackTargetForProgress(
contentId = item.stableKey,
videoId = item.playbackVideoId(file),
),
)
assertEquals("https://resolved.example/movie.mkv", target.file.playbackUrl)
}
@Test
fun `provider poster urls are mapped for cloud services`() {
assertEquals(
@ -155,6 +208,9 @@ private class FakeCloudProviderApi(
override val provider: DebridProvider,
private val items: List<CloudLibraryItem>,
) : CloudLibraryProviderApi {
var resolvePlaybackCalls: Int = 0
private set
override suspend fun listItems(apiKey: String): Result<List<CloudLibraryItem>> =
Result.success(items)
@ -162,8 +218,10 @@ private class FakeCloudProviderApi(
apiKey: String,
item: CloudLibraryItem,
file: CloudLibraryFile,
): CloudLibraryPlaybackResult =
CloudLibraryPlaybackResult.Success(url = "https://example.test/${item.id}/${file.id}")
): CloudLibraryPlaybackResult {
resolvePlaybackCalls += 1
return CloudLibraryPlaybackResult.Success(url = "https://example.test/${item.id}/${file.id}")
}
}
private fun cloudProvider(id: String, name: String): DebridProvider =