mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-14 22:12:02 +00:00
fix(downloads): passing resume position
This commit is contained in:
parent
7437f54ab8
commit
7c2cad51b9
6 changed files with 33 additions and 5 deletions
|
|
@ -1,20 +1,24 @@
|
||||||
package com.nuvio.app.features.player
|
package com.nuvio.app.features.player
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.media3.datasource.DataSource
|
import androidx.media3.datasource.DataSource
|
||||||
|
import androidx.media3.datasource.DefaultDataSource
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource
|
import androidx.media3.datasource.DefaultHttpDataSource
|
||||||
import com.nuvio.app.features.trailer.YoutubeChunkedDataSourceFactory
|
import com.nuvio.app.features.trailer.YoutubeChunkedDataSourceFactory
|
||||||
|
|
||||||
internal object PlatformPlaybackDataSourceFactory {
|
internal object PlatformPlaybackDataSourceFactory {
|
||||||
fun create(
|
fun create(
|
||||||
|
context: Context,
|
||||||
defaultRequestHeaders: Map<String, String>,
|
defaultRequestHeaders: Map<String, String>,
|
||||||
defaultResponseHeaders: Map<String, String>,
|
defaultResponseHeaders: Map<String, String>,
|
||||||
useYoutubeChunkedPlayback: Boolean,
|
useYoutubeChunkedPlayback: Boolean,
|
||||||
): DataSource.Factory {
|
): DataSource.Factory {
|
||||||
val baseFactory: DataSource.Factory = if (useYoutubeChunkedPlayback) {
|
val networkFactory: DataSource.Factory = if (useYoutubeChunkedPlayback) {
|
||||||
YoutubeChunkedDataSourceFactory(defaultRequestHeaders = defaultRequestHeaders)
|
YoutubeChunkedDataSourceFactory(defaultRequestHeaders = defaultRequestHeaders)
|
||||||
} else {
|
} else {
|
||||||
DefaultHttpDataSource.Factory().setDefaultRequestProperties(defaultRequestHeaders)
|
DefaultHttpDataSource.Factory().setDefaultRequestProperties(defaultRequestHeaders)
|
||||||
}
|
}
|
||||||
|
val baseFactory: DataSource.Factory = DefaultDataSource.Factory(context, networkFactory)
|
||||||
return if (defaultResponseHeaders.isEmpty()) {
|
return if (defaultResponseHeaders.isEmpty()) {
|
||||||
baseFactory
|
baseFactory
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,8 @@ internal actual object DownloadsPlatformDownloader {
|
||||||
tempFile.delete()
|
tempFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuccess(destination.toURI().toString(), totalBytes)
|
val finalSize = destination.length()
|
||||||
|
onSuccess(destination.toURI().toString(), totalBytes ?: finalSize)
|
||||||
}
|
}
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
tempFile.delete()
|
tempFile.delete()
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ actual fun PlatformPlayerSurface(
|
||||||
.setTsExtractorTimestampSearchBytes(1500 * TsExtractor.TS_PACKET_SIZE)
|
.setTsExtractorTimestampSearchBytes(1500 * TsExtractor.TS_PACKET_SIZE)
|
||||||
|
|
||||||
val dataSourceFactory = PlatformPlaybackDataSourceFactory.create(
|
val dataSourceFactory = PlatformPlaybackDataSourceFactory.create(
|
||||||
|
context = context,
|
||||||
defaultRequestHeaders = sanitizedSourceHeaders,
|
defaultRequestHeaders = sanitizedSourceHeaders,
|
||||||
defaultResponseHeaders = sanitizedSourceResponseHeaders,
|
defaultResponseHeaders = sanitizedSourceResponseHeaders,
|
||||||
useYoutubeChunkedPlayback = useYoutubeChunkedPlayback,
|
useYoutubeChunkedPlayback = useYoutubeChunkedPlayback,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
package com.nuvio.app.features.player
|
package com.nuvio.app.features.player
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.media3.datasource.DataSource
|
import androidx.media3.datasource.DataSource
|
||||||
|
import androidx.media3.datasource.DefaultDataSource
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource
|
import androidx.media3.datasource.DefaultHttpDataSource
|
||||||
|
|
||||||
internal object PlatformPlaybackDataSourceFactory {
|
internal object PlatformPlaybackDataSourceFactory {
|
||||||
fun create(
|
fun create(
|
||||||
|
context: Context,
|
||||||
defaultRequestHeaders: Map<String, String>,
|
defaultRequestHeaders: Map<String, String>,
|
||||||
defaultResponseHeaders: Map<String, String>,
|
defaultResponseHeaders: Map<String, String>,
|
||||||
useYoutubeChunkedPlayback: Boolean,
|
useYoutubeChunkedPlayback: Boolean,
|
||||||
): DataSource.Factory {
|
): DataSource.Factory {
|
||||||
val baseFactory = DefaultHttpDataSource.Factory()
|
val httpFactory = DefaultHttpDataSource.Factory()
|
||||||
.setDefaultRequestProperties(defaultRequestHeaders)
|
.setDefaultRequestProperties(defaultRequestHeaders)
|
||||||
|
val baseFactory: DataSource.Factory = DefaultDataSource.Factory(context, httpFactory)
|
||||||
return if (defaultResponseHeaders.isEmpty()) {
|
return if (defaultResponseHeaders.isEmpty()) {
|
||||||
baseFactory
|
baseFactory
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1250,6 +1250,11 @@ private fun MainAppContent(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
onOpenDownload = { item ->
|
onOpenDownload = { item ->
|
||||||
val sourceUrl = item.localFileUri ?: return@DownloadsScreen
|
val sourceUrl = item.localFileUri ?: return@DownloadsScreen
|
||||||
|
val resumeEntry = item.videoId
|
||||||
|
.takeIf { it.isNotBlank() }
|
||||||
|
?.let(WatchProgressRepository::progressForVideo)
|
||||||
|
?.takeIf { it.isResumable }
|
||||||
|
|
||||||
val launchId = PlayerLaunchStore.put(
|
val launchId = PlayerLaunchStore.put(
|
||||||
PlayerLaunch(
|
PlayerLaunch(
|
||||||
title = item.title,
|
title = item.title,
|
||||||
|
|
@ -1271,7 +1276,8 @@ private fun MainAppContent(
|
||||||
videoId = item.videoId,
|
videoId = item.videoId,
|
||||||
parentMetaId = item.parentMetaId,
|
parentMetaId = item.parentMetaId,
|
||||||
parentMetaType = item.parentMetaType,
|
parentMetaType = item.parentMetaType,
|
||||||
initialPositionMs = 0L,
|
initialPositionMs = resumeEntry?.lastPositionMs?.takeIf { it > 0L } ?: 0L,
|
||||||
|
initialProgressFraction = resumeEntry?.progressFraction?.takeIf { it > 0f },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
navController.navigate(PlayerRoute(launchId = launchId))
|
navController.navigate(PlayerRoute(launchId = launchId))
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,8 @@ internal actual object DownloadsPlatformDownloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
val localFileUri = NSURL.fileURLWithPath(destinationPath).absoluteString ?: "file://$destinationPath"
|
val localFileUri = NSURL.fileURLWithPath(destinationPath).absoluteString ?: "file://$destinationPath"
|
||||||
onSuccess(localFileUri, totalBytes)
|
val finalSize = fileSizeOrNull(destinationPath)
|
||||||
|
onSuccess(localFileUri, totalBytes ?: finalSize)
|
||||||
} catch (_: CancellationException) {
|
} catch (_: CancellationException) {
|
||||||
removePathIfExists(tempPath)
|
removePathIfExists(tempPath)
|
||||||
} catch (error: Throwable) {
|
} catch (error: Throwable) {
|
||||||
|
|
@ -174,6 +175,17 @@ private suspend fun writeChannelToFile(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalForeignApi::class)
|
||||||
|
private fun fileSizeOrNull(path: String): Long? {
|
||||||
|
val attrs = NSFileManager.defaultManager.attributesOfItemAtPath(path, error = null)
|
||||||
|
val value = attrs?.get("NSFileSize")
|
||||||
|
return when (value) {
|
||||||
|
is Long -> value
|
||||||
|
is Number -> value.toLong()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.toLocalPath(): String? {
|
private fun String.toLocalPath(): String? {
|
||||||
if (startsWith("file://")) {
|
if (startsWith("file://")) {
|
||||||
return removePrefix("file://")
|
return removePrefix("file://")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue