Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.lagradost.cloudstream3.ui.player

import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.CloudStreamApp.Companion.context
import com.lagradost.cloudstream3.services.DownloadQueueService
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.downloader.LocalDownloadPolicy
import com.lagradost.cloudstream3.utils.downloader.DownloadObjects
import com.lagradost.cloudstream3.utils.downloader.VideoDownloadManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException

class LocalFirstGenerator(private val remote: RepoLinkGenerator) : VideoGenerator<ResultEpisode>(remote.videos) {
override val hasCache = remote.hasCache
override val canSkipLoading = remote.canSkipLoading
override fun getId(index: Int) = remote.getId(index)

override suspend fun generateLinks(
clearCache: Boolean,
sourceTypes: Set<ExtractorLinkType>,
callback: (Pair<ExtractorLink?, ExtractorUri?>) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
offset: Int,
isCasting: Boolean,
): Boolean {
val ctx = context
val episode = videos.getOrNull(offset)
if (!isCasting && !clearCache && ctx != null && episode != null &&
PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("prefer_local_downloads", true)) {
val local = withContext(Dispatchers.IO) {
try {
val info = VideoDownloadManager.getDownloadFileInfo(ctx, episode.id) ?: return@withContext null
val completed = ctx.getKey<Long>(VideoDownloadManager.KEY_COMPLETED_DOWNLOAD_BYTES, episode.id.toString())
val busy = DownloadQueueService.downloadInstances.value.any { it.downloadQueueWrapper.id == episode.id }
if (!LocalDownloadPolicy.isComplete(info.fileLength, info.totalBytes, completed, busy)) return@withContext null
ctx.contentResolver.openAssetFileDescriptor(info.path, "r")?.use { } ?: return@withContext null
val meta = ctx.getKey<DownloadObjects.DownloadedFileInfo>(VideoDownloadManager.KEY_DOWNLOAD_INFO, episode.id.toString())
?: return@withContext null
ExtractorUri(uri = info.path, name = episode.name ?: remote.page?.name.orEmpty(),
id = episode.id, parentId = episode.parentId, season = episode.season, episode = episode.episode,
headerName = remote.page?.name, tvType = remote.page?.type,
basePath = meta.basePath, relativePath = meta.relativePath, displayName = meta.displayName)
} catch (_: IOException) { null } catch (_: SecurityException) { null }
}
if (local != null) return DownloadFileGenerator(listOf(local)).generateLinks(
false, sourceTypes, callback, subtitleCallback, 0, false)
}
return remote.generateLinks(clearCache, sourceTypes, callback, subtitleCallback, offset, isCasting)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1565,7 +1565,7 @@ class ResultViewModel2 : ViewModel() {
activity?.navigate(
R.id.global_to_navigation_player,
GeneratorPlayer.newInstance(
generator, index,list
com.lagradost.cloudstream3.ui.player.LocalFirstGenerator(generator), index,list
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,16 @@ object SettingsGeneralScreen : SearchableSettings {
}.sortedBy { fromTagToLanguageName(it) ?: it }

return persistentListOf(
Preference.PreferenceGroup(
title = stringResource(R.string.local_playback_options_title),
preferenceItems = persistentListOf(
Preference.PreferenceItem.SwitchPreference(
preference = settings.general.preferLocalDownloads,
title = stringResource(R.string.prefer_local_downloads_title),
subtitle = stringResource(R.string.prefer_local_downloads_summary),
),
),
),
Preference.PreferenceGroup(title = stringResource(R.string.extension_language), preferenceItems = persistentListOf(
Preference.PreferenceItem.ListPreference(
preference = settings.general.locale,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ object VideoDownloadManager {

const val KEY_RESUME_PACKAGES = "download_resume_2"
const val KEY_DOWNLOAD_INFO = "download_info"
const val KEY_COMPLETED_DOWNLOAD_BYTES = "completed_download_bytes"

/** A key to save all the downloads which have not yet started and those currently running, using [DownloadQueueWrapper]
* [KEY_RESUME_PACKAGES] can store keys which should not be automatically queued, unlike this key.
Expand Down Expand Up @@ -639,6 +640,7 @@ object VideoDownloadManager {
}

fun setDownloadFileInfoTemplate(template: DownloadedFileInfo) {
if (id != null) removeKey(KEY_COMPLETED_DOWNLOAD_BYTES, id.toString())
downloadFileInfoTemplate = template
updateFileInfo()
}
Expand All @@ -665,6 +667,9 @@ object VideoDownloadManager {
get() = internalType
set(value) {
internalType = value
if (id != null && value == DownloadType.IsDone) {
setKey(KEY_COMPLETED_DOWNLOAD_BYTES, id.toString(), bytesWritten)
}
notify()
}

Expand Down Expand Up @@ -1617,7 +1622,10 @@ object VideoDownloadManager {

private fun deleteFileAndUpdateSettings(context: Context, id: Int): Boolean {
val success = deleteFile(context, id)
if (success) context.removeKey(KEY_DOWNLOAD_INFO, id.toString())
if (success) {
context.removeKey(KEY_DOWNLOAD_INFO, id.toString())
context.removeKey(KEY_COMPLETED_DOWNLOAD_BYTES, id.toString())
}
return success
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.lagradost.cloudstream3.utils.downloader

internal object LocalDownloadPolicy {
fun isComplete(length: Long, expected: Long, completedLength: Long?, busy: Boolean): Boolean =
!busy && length > 0 && (completedLength == length || (expected > 0 && length == expected))
}
5 changes: 5 additions & 0 deletions app/src/main/res/values-es/local_playback_options.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<resources>
<string name="local_playback_options_title">Reproducción local</string>
<string name="prefer_local_downloads_title">Reproducir primero las descargas</string>
<string name="prefer_local_downloads_summary">Usa una descarga completa y accesible antes de solicitar enlaces. Desactívalo para priorizar el streaming.</string>
</resources>
5 changes: 5 additions & 0 deletions app/src/main/res/values/local_playback_options.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<resources>
<string name="local_playback_options_title">Local playback</string>
<string name="prefer_local_downloads_title">Play downloaded files first</string>
<string name="prefer_local_downloads_summary">Use a complete, readable download before requesting streaming links. Turn off to keep streaming first.</string>
</resources>
3 changes: 3 additions & 0 deletions app/src/main/res/xml/settings_general.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="@string/local_playback_options_title">
<SwitchPreferenceCompat android:key="prefer_local_downloads" android:title="@string/prefer_local_downloads_title" android:summary="@string/prefer_local_downloads_summary" android:defaultValue="true" />
</PreferenceCategory>
<Preference
android:icon="@drawable/ic_baseline_language_24"
android:key="@string/locale_key"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.lagradost.cloudstream3.utils.downloader

import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test

class LocalDownloadPolicyTest {
@Test fun acceptsCompletedLegacyFile() {
assertTrue(LocalDownloadPolicy.isComplete(1024, 1024, null, false))
}
@Test fun acceptsRecordedCompletionWithUnknownContentLength() {
assertTrue(LocalDownloadPolicy.isComplete(1024, 0, 1024, false))
}
@Test fun rejectsPartialEmptyAndActiveFiles() {
assertFalse(LocalDownloadPolicy.isComplete(512, 1024, null, false))
assertFalse(LocalDownloadPolicy.isComplete(0, 0, 0, false))
assertFalse(LocalDownloadPolicy.isComplete(1024, 1024, 1024, true))
}
@Test fun rejectsChangedLengthAndUnconfirmedUnknownLength() {
assertFalse(LocalDownloadPolicy.isComplete(512, 0, 1024, false))
assertFalse(LocalDownloadPolicy.isComplete(1024, 0, null, false))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class PlayerPreferences(preferences: PreferenceStore) {
}

class GeneralPreferences(preferences: PreferenceStore) {
val preferLocalDownloads = preferences.getBoolean("prefer_local_downloads", true)
val locale = preferences.getString("app_locale", "")
val bananas = preferences.getInt("benene_count", 0)
val parallelDownloads = preferences.getInt("download_parallel_key", 3)
Expand Down
Loading