From 1d5561068574b5c783e052751ee16fcc8ca11c93 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Sun, 8 Sep 2024 22:51:19 +0200
Subject: [PATCH 001/956] initial start on the VideoClickAction api
---
app/src/main/AndroidManifest.xml | 9 +---
.../lagradost/cloudstream3/MainActivity.kt | 2 +
.../cloudstream3/actions/OpenInAppAction.kt | 48 +++++++++++++++++++
.../cloudstream3/actions/VideoClickAction.kt | 44 +++++++++++++++++
.../lagradost/cloudstream3/plugins/Plugin.kt | 14 ++++++
.../cloudstream3/plugins/PluginManager.kt | 7 +++
.../cloudstream3/ui/result/EpisodeAdapter.kt | 5 ++
.../ui/result/ResultViewModel2.kt | 37 ++++++++++++++
8 files changed, 158 insertions(+), 8 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1aeef5550..a04504acd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,7 +17,7 @@
-
+
@@ -30,13 +30,6 @@
android:name="android.software.leanback"
android:required="false" />
-
-
-
-
-
-
-
? = null
+
//TODO REFACTOR AF
open class ResultResume(
val packageString: String,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
new file mode 100644
index 000000000..4673af3f3
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -0,0 +1,48 @@
+package com.lagradost.cloudstream3.actions
+
+import android.app.Activity
+import android.content.ComponentName
+import android.content.Intent
+import com.lagradost.cloudstream3.MainActivity.Companion.activityResultLauncher
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
+
+abstract class OpenInAppAction(
+ open val appName: UiText,
+ open val packageName: String,
+ private val intentClass: String?,
+ private val action: String = Intent.ACTION_VIEW
+): VideoClickAction() {
+ override val name: UiText
+ get() = txt(R.string.episode_action_play_in_format, appName)
+
+ override fun shouldShow(activity: Activity?, video: ResultEpisode) = activity?.isAppInstalled(packageName) == true
+
+ override fun runAction(
+ activity: Activity?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (activity == null) return
+ val intent = Intent(action)
+ intent.setPackage(packageName)
+ if (intentClass != null) {
+ intent.component = ComponentName(packageName, intentClass)
+ }
+ putExtra(activity, intent, video, result, index)
+
+ // TODO: understand the spaghetti that is ResultResume
+ activityResultLauncher?.launch(intent)
+ }
+
+ /**
+ * Before intent is sent, this function is called to put extra data into the intent.
+ * @see VideoClickAction.runAction
+ * */
+ abstract fun putExtra(activity: Activity, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
new file mode 100644
index 000000000..ab188a702
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -0,0 +1,44 @@
+package com.lagradost.cloudstream3.actions
+
+import android.app.Activity
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+object VideoClickActionHolder {
+ val allVideoClickActions = threadSafeListOf()
+
+ private const val ACTION_ID_OFFSET = 1000
+
+ fun makeOptionMap(activity: Activity?, video: ResultEpisode) = allVideoClickActions
+ .filter { it.shouldShow(activity, video) }
+ .mapIndexed { index, it -> it.name to index + ACTION_ID_OFFSET }
+
+ fun getActionById(id: Int): VideoClickAction? = allVideoClickActions.getOrNull(id - ACTION_ID_OFFSET)
+}
+
+abstract class VideoClickAction {
+ abstract val name: UiText
+
+ /** if true, the app will show dialog to select source - result.links[index] */
+ val oneSource : Boolean = false
+
+ /** Which type of sources this action can handle. */
+ val sourceTypes: Set = ExtractorLinkType.entries.toSet()
+
+ /** Determines which plugin a given provider is from. This is the full path to the plugin. */
+ var sourcePlugin: String? = null
+
+ abstract fun shouldShow(activity: Activity?, video: ResultEpisode): Boolean
+
+ /**
+ * This function is called when the action is clicked.
+ * @param activity The current activity
+ * @param video The episode/movie that was clicked
+ * @param result The result of the link loading, contains video & subtitle links
+ * @param index if oneSource is true, this is the index of the selected source
+ */
+ abstract fun runAction(activity: Activity?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
index fc8365876..e35ae24b9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
@@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.extractorApis
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
const val PLUGIN_TAG = "PluginInstance"
@@ -52,6 +54,18 @@ abstract class Plugin {
extractorApis.add(element)
}
+ /**
+ * Used to register VideoClickAction instances
+ * @param element VideoClickAction you want to register
+ */
+ fun registerVideoClickAction(element: VideoClickAction) {
+ Log.i(PLUGIN_TAG, "Adding ${element.name} VideoClickAction")
+ element.sourcePlugin = this.filename
+ synchronized(VideoClickActionHolder.allVideoClickActions) {
+ VideoClickActionHolder.allVideoClickActions.add(element)
+ }
+ }
+
class Manifest {
@JsonProperty("name")
var name: String? = null
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index c7f416883..8535592d4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -24,6 +24,8 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
@@ -583,8 +585,13 @@ object PluginManager {
synchronized(APIHolder.allProviders) {
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.filename }
}
+
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.filename }
+ synchronized(VideoClickActionHolder.allVideoClickActions) {
+ VideoClickActionHolder.allVideoClickActions.removeIf { action: VideoClickAction -> action.sourcePlugin == plugin.filename }
+ }
+
classLoaders.values.removeIf { v -> v == plugin }
plugins.remove(absolutePath)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 4cd9cc9ea..a19adc4e8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -30,6 +30,11 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
+/**
+ * Ids >= 1000 are reserved for VideoClickActions
+ * @see VideoClickActionHolder
+ */
+
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2
const val ACTION_PLAY_EPISODE_IN_BROWSER = 3
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index a29941d11..99fc65f81 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -41,6 +41,7 @@ import com.lagradost.cloudstream3.MainActivity.Companion.VLC_COMPONENT
import com.lagradost.cloudstream3.MainActivity.Companion.VLC_PACKAGE
import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO
import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO_CAST_PACKAGE
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager
@@ -1576,6 +1577,9 @@ class ResultViewModel2 : ViewModel() {
when (click.action) {
ACTION_SHOW_OPTIONS -> {
val options = mutableListOf>()
+
+ VideoClickActionHolder.makeOptionMap(activity, click.data)
+
if (activity?.isConnectedToChromecast() == true) {
options.addAll(
listOf(
@@ -1615,6 +1619,10 @@ class ResultViewModel2 : ViewModel() {
)
)
+ options.addAll(
+ VideoClickActionHolder.makeOptionMap(activity, click.data)
+ )
+
// Do not add mark as watched on movies
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
val isWatched =
@@ -1933,6 +1941,35 @@ class ResultViewModel2 : ViewModel() {
// Kinda dirty to reload all episodes :(
reloadEpisodes()
}
+
+ else -> {
+ val action = VideoClickActionHolder.getActionById(click.action) ?: return
+
+ // TODO: use action.sourceTypes
+ if (action.oneSource) {
+ loadLinks(click.data, isVisible = true, LoadType.ExternalApp) { links ->
+ action.runAction(
+ activity,
+ click.data,
+ links,
+ null
+ )
+ }
+ } else {
+ acquireSingleLink(
+ click.data,
+ LoadType.ExternalApp,
+ action.name
+ ) { (result, index) ->
+ action.runAction(
+ activity,
+ click.data,
+ result,
+ index
+ )
+ }
+ }
+ }
}
}
From 9505ca259255c545471278a071339d8dabf47041 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Mon, 9 Sep 2024 17:21:45 +0200
Subject: [PATCH 002/956] move all app actions to new api, handle some todos
---
.../lagradost/cloudstream3/CommonActivity.kt | 28 +-
.../lagradost/cloudstream3/MainActivity.kt | 105 +-----
.../cloudstream3/actions/OpenInAppAction.kt | 93 ++++-
.../cloudstream3/actions/VideoClickAction.kt | 32 +-
.../cloudstream3/actions/temp/MpvKtPackage.kt | 67 ++++
.../cloudstream3/actions/temp/MpvPackage.kt | 61 ++++
.../actions/temp/PlayInBrowserAction.kt | 41 +++
.../cloudstream3/actions/temp/TestAction.kt | 47 +++
.../cloudstream3/actions/temp/VlcPackage.kt | 55 +++
.../actions/temp/WebVideoCastPackage.kt | 61 ++++
.../cloudstream3/ui/ControllerActivity.kt | 16 +-
.../ui/player/DownloadFileGenerator.kt | 6 +-
.../ui/player/ExtractorLinkGenerator.kt | 9 +-
.../cloudstream3/ui/player/IGenerator.kt | 65 ++--
.../cloudstream3/ui/player/LinkGenerator.kt | 6 +-
.../ui/player/PlayerGeneratorViewModel.kt | 7 +-
.../ui/player/RepoLinkGenerator.kt | 10 +-
.../ui/result/ResultViewModel2.kt | 345 ++----------------
18 files changed, 552 insertions(+), 502 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index ee3a5d122..50e6d8c98 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -30,15 +30,14 @@ import com.google.android.material.chip.ChipGroup
import com.google.android.material.navigationrail.NavigationRailView
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
-import com.lagradost.cloudstream3.MainActivity.Companion.resumeApps
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.databinding.ToastBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
-import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.settings.Globals.updateTv
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
-import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.hasPIPPermission
@@ -218,20 +217,15 @@ object CommonActivity {
componentActivity.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance())
- for (resumeApp in resumeApps) {
- resumeApp.launcher =
- componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- val resultCode = result.resultCode
- val data = result.data
- if (resultCode == AppCompatActivity.RESULT_OK && data != null && resumeApp.position != null && resumeApp.duration != null) {
- val pos = resumeApp.getPosition(data)
- val dur = resumeApp.getDuration(data)
- if (dur > 0L && pos > 0L)
- DataStoreHelper.setViewPos(getKey(resumeApp.lastId), pos, dur)
- removeKey(resumeApp.lastId)
- ResultFragment.updateUI()
- }
- }
+ MainActivity.activityResultLauncher = componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
+ val actionUid = getKey("last_click_action") ?: return@registerForActivityResult
+ Log.d(TAG, "Loading action $actionUid result handler")
+ val action = VideoClickActionHolder.getByUniqueId(actionUid) as? OpenInAppAction ?: return@registerForActivityResult
+ action.onResult(act, result.data)
+ removeKey("last_click_action")
+ removeKey("last_opened_id")
+ }
}
// Ask for notification permissions on Android 13
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index d99283593..0625f572e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -198,110 +198,7 @@ import kotlin.system.exitProcess
class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback {
companion object {
- const val VLC_PACKAGE = "org.videolan.vlc"
- const val MPV_PACKAGE = "is.xyz.mpv"
- const val MPV_YTDL_PACKAGE = "is.xyz.mpv.ytdl"
- const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
-
- val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
- val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
- val MPV_YTDL_COMPONENT = ComponentName(MPV_YTDL_PACKAGE, "$MPV_PACKAGE.MPVActivity")
-
- val activityResultLauncher: ActivityResultLauncher? = null
-
- //TODO REFACTOR AF
- open class ResultResume(
- val packageString: String,
- val action: String = Intent.ACTION_VIEW,
- val position: String? = null,
- val duration: String? = null,
- var launcher: ActivityResultLauncher? = null,
- ) {
- val defaultTime = -1L
-
- val lastId get() = "${packageString}_last_open_id"
- suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) {
- val intent = Intent(action)
-
- if (id != null)
- setKey(lastId, id)
- else
- removeKey(lastId)
-
- intent.setPackage(packageString)
- callback.invoke(intent)
- launcher?.launch(intent)
- }
-
- open fun getPosition(intent: Intent?): Long {
- return defaultTime
- }
-
- open fun getDuration(intent: Intent?): Long {
- return defaultTime
- }
- }
-
- val VLC = object : ResultResume(
- VLC_PACKAGE,
- // Android 13 intent restrictions fucks up specifically launching the VLC player
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- "org.videolan.vlc.player.result"
- } else {
- Intent.ACTION_VIEW
- },
- "extra_position",
- "extra_duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime
- }
- }
-
- val MPV = object : ResultResume(
- MPV_PACKAGE,
- //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
- position = "position",
- duration = "duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
- }
-
- val MPV_YTDL = object : ResultResume(
- MPV_YTDL_PACKAGE,
- //"is.xyz.mpv.ytdl/is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
- position = "position",
- duration = "duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
- }
-
- val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
-
- val resumeApps = arrayOf(
- VLC, MPV, MPV_YTDL, WEB_VIDEO
- )
-
+ var activityResultLauncher: ActivityResultLauncher? = null
const val TAG = "MAINACT"
const val ANIMATED_OUTLINE: Boolean = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 4673af3f3..534102fea 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -1,20 +1,86 @@
package com.lagradost.cloudstream3.actions
import android.app.Activity
+import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Intent
+import android.widget.Toast
+import androidx.core.content.FileProvider
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
+import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.activityResultLauncher
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
+import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+
+fun updateDurationAndPosition(position: Long, duration: Long) {
+ if (position <= 0 || duration <= 0) return
+ DataStoreHelper.setViewPos(getKey("last_opened_id"), position, duration)
+ ResultFragment.updateUI()
+}
+
+/**
+ * Util method that may be helpful for creating intents for apps that support m3u8 files.
+ * All sources are written to a temporary m3u8 file, which is then sent to the app.
+ */
+fun makeTempM3U8Intent(activity: Activity,
+ intent: Intent,
+ result: LinkLoadingResult) {
+ intent.apply {
+ addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ }
+
+ val outputDir = activity.cacheDir
+
+ if (result.links.size == 1) {
+ intent.setDataAndType(result.links.first().url.toUri(), "video/*")
+ } else {
+ val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
+
+ var text = "#EXTM3U"
+
+ //With subtitles it doesn't work for no reason :(
+ /*for (sub in result.subs) {
+ val normalizedName = sub.name.replace("[^a-zA-Z0-9 ]".toRegex(), "")
+ val language = fromLanguageToTwoLetters(sub.name, true) ?: "und"
+ text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${language}\",URI=\"${sub.url}\""
+ }*/
+
+ for (link in result.links) {
+ text += "\n#EXTINF:, ${link.name}\n${link.url}"
+ }
+ outputFile.writeText(text)
+
+ intent.setDataAndType(
+ FileProvider.getUriForFile(
+ activity,
+ activity.applicationContext.packageName + ".provider",
+ outputFile
+ ), "video/*"
+ )
+ }
+}
abstract class OpenInAppAction(
open val appName: UiText,
open val packageName: String,
- private val intentClass: String?,
+ private val intentClass: String? = null,
private val action: String = Intent.ACTION_VIEW
): VideoClickAction() {
override val name: UiText
@@ -35,9 +101,21 @@ abstract class OpenInAppAction(
intent.component = ComponentName(packageName, intentClass)
}
putExtra(activity, intent, video, result, index)
-
- // TODO: understand the spaghetti that is ResultResume
- activityResultLauncher?.launch(intent)
+ setKey("last_opened_id", video.id)
+ try {
+ CoroutineScope(Dispatchers.IO).launch {
+ activityResultLauncher?.launch(intent)
+ }
+ } catch (t: Throwable) {
+ logError(t)
+ main {
+ if (t is ActivityNotFoundException) {
+ showToast(txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
+ } else {
+ showToast(t.toString(), Toast.LENGTH_LONG)
+ }
+ }
+ }
}
/**
@@ -45,4 +123,11 @@ abstract class OpenInAppAction(
* @see VideoClickAction.runAction
* */
abstract fun putExtra(activity: Activity, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+
+ /**
+ * This function is called when the app is opened again after the intent was sent.
+ * You can use it to for example read duration and position.
+ * @see updateDurationAndPosition
+ */
+ abstract fun onResult(activity: Activity, intent: Intent?)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index ab188a702..75d3747ae 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -1,6 +1,14 @@
package com.lagradost.cloudstream3.actions
import android.app.Activity
+import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
+import com.lagradost.cloudstream3.actions.temp.MpvKtPreviewPackage
+import com.lagradost.cloudstream3.actions.temp.MpvPackage
+import com.lagradost.cloudstream3.actions.temp.MpvYTDLPackage
+import com.lagradost.cloudstream3.actions.temp.PlayInBrowserAction
+import com.lagradost.cloudstream3.actions.temp.TestAction
+import com.lagradost.cloudstream3.actions.temp.VlcPackage
+import com.lagradost.cloudstream3.actions.temp.WebVideoCastPackage
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.UiText
@@ -8,29 +16,43 @@ import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLinkType
object VideoClickActionHolder {
- val allVideoClickActions = threadSafeListOf()
+ val allVideoClickActions = threadSafeListOf(
+ PlayInBrowserAction(), VlcPackage(), TestAction(),
+ MpvPackage(), MpvYTDLPackage(),
+ WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage()
+ )
private const val ACTION_ID_OFFSET = 1000
fun makeOptionMap(activity: Activity?, video: ResultEpisode) = allVideoClickActions
- .filter { it.shouldShow(activity, video) }
- .mapIndexed { index, it -> it.name to index + ACTION_ID_OFFSET }
+ // We need to have index before filtering
+ .mapIndexed { index, it -> it to index + ACTION_ID_OFFSET }
+ .filter { it.first.shouldShow(activity, video) }
+ .map { it.first.name to it.second }
+
fun getActionById(id: Int): VideoClickAction? = allVideoClickActions.getOrNull(id - ACTION_ID_OFFSET)
+
+ fun getByUniqueId(uniqueId: String): VideoClickAction? = allVideoClickActions.firstOrNull { it.uniqueId() == uniqueId }
}
abstract class VideoClickAction {
abstract val name: UiText
/** if true, the app will show dialog to select source - result.links[index] */
- val oneSource : Boolean = false
+ open val oneSource : Boolean = false
+
+ /** if true, this action could be selected as default one press action in settings */
+ open val canBeDefault: Boolean = false
/** Which type of sources this action can handle. */
- val sourceTypes: Set = ExtractorLinkType.entries.toSet()
+ open val sourceTypes: Set = ExtractorLinkType.entries.toSet()
/** Determines which plugin a given provider is from. This is the full path to the plugin. */
var sourcePlugin: String? = null
+ fun uniqueId() = "$sourcePlugin:${this::class.simpleName}"
+
abstract fun shouldShow(activity: Activity?, video: ResultEpisode): Boolean
/**
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
new file mode 100644
index 000000000..57f564d24
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
@@ -0,0 +1,67 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class MpvKtPreviewPackage: MpvKtPackage(
+ appName = "mpvKt Preview",
+ packageName = "live.mehiz.mpvkt.preview",
+)
+
+open class MpvKtPackage(
+ appName: String = "mpvKt",
+ packageName: String = "live.mehiz.mpvkt",
+): OpenInAppAction(
+ appName = txt(appName),
+ packageName = packageName,
+ intentClass = "live.mehiz.mpvkt.ui.player.PlayerActivity"
+) {
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun putExtra(
+ activity: Activity,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links[index ?: 0]
+
+ intent.apply {
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ putExtra("subs.name", result.subs.map { it.name }.toTypedArray())
+ putExtra("subs.filename", result.subs.map { it.name }.toTypedArray())
+ setDataAndType(Uri.parse(link.url), "video/*")
+ // m3u8 plays, but changing sources feature is not available
+ // makeTempM3U8Intent(activity, this, result)
+ putExtra("secure_uri", true)
+ putExtra("return_result", true)
+ //putExtra("headers", link.headers.flatMap { listOf(it.key, it.value) }.toTypedArray())
+ val position = getViewPos(video.id)?.position
+ if (position != null)
+ putExtra("position", position.toInt())
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getIntExtra("position", -1)?.toLong() ?: -1
+ val duration = intent?.getIntExtra("duration", -1)?.toLong() ?: -1
+ updateDurationAndPosition(position, duration)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
new file mode 100644
index 000000000..7ff05a3f1
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -0,0 +1,61 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Intent
+import androidx.core.net.toUri
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class MpvYTDLPackage : MpvPackage("MPV YTDL", "is.xyz.mpv.ytdl") {
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+}
+
+open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv"): OpenInAppAction(
+ txt(appName),
+ packageName,
+ "is.xyz.mpv.MPVActivity"
+) {
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun putExtra(
+ activity: Activity,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ intent.apply {
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ putExtra("subs.name", result.subs.map { it.name }.toTypedArray())
+ putExtra("subs.filename", result.subs.map { it.name }.toTypedArray())
+ makeTempM3U8Intent(activity, this, result)
+ putExtra("secure_uri", true)
+ putExtra("return_result", true)
+ val position = getViewPos(video.id)?.position
+ if (position != null)
+ putExtra("position", position.toInt())
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getIntExtra("position", -1) ?: -1
+ val duration = intent?.getIntExtra("duration", -1) ?: -1
+ Log.d("MPV", "Position: $position, Duration: $duration")
+ updateDurationAndPosition(position.toLong(), duration.toLong())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
new file mode 100644
index 000000000..6d6f78f51
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -0,0 +1,41 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.mvvm.logError
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class PlayInBrowserAction: VideoClickAction() {
+ override val name = txt("Play in browser")
+
+ override val oneSource = true
+
+ override val sourceTypes: Set = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun shouldShow(activity: Activity?, video: ResultEpisode) = true
+
+ override fun runAction(
+ activity: Activity?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (index == null) return
+ try {
+ val i = Intent(Intent.ACTION_VIEW)
+ i.data = Uri.parse(result.links[index].url)
+ activity?.startActivity(i)
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
new file mode 100644
index 000000000..3c7464a05
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
@@ -0,0 +1,47 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.AppUtils.toJson
+
+class TestAction: VideoClickAction() {
+ override val name = txt("Test action")
+
+ override fun shouldShow(activity: Activity?, video: ResultEpisode): Boolean {
+ return true
+ }
+
+ override fun runAction(
+ activity: Activity?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ // Show dialog box
+ val text = """
+Result links:
+${result.links.joinToString("\n") { if (it.url.length > 50) it.url.take(25) + "..." + it.url.takeLast(25) else it.url }}
+Result subs:
+${result.subs.joinToString("\n") { if (it.url.length > 50) it.url.take(25) + "..." + it.url.takeLast(25) else it.url }}
+Video:
+${video.toJson()}
+ """.trim()
+
+
+ activity?.runOnUiThread {
+ val dialog = android.app.AlertDialog.Builder(activity)
+ dialog.apply {
+ setTitle("Action: ${video.name}")
+ setMessage(text)
+ setPositiveButton("OK") { dialog, _ ->
+ dialog.dismiss()
+ }
+ }
+ dialog.show()
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
new file mode 100644
index 000000000..7f3b34d01
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -0,0 +1,55 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Build
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+
+class VlcPackage: OpenInAppAction(
+ appName = txt("VLC"),
+ packageName = "org.videolan.vlc",
+ intentClass = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ "org.videolan.vlc.gui.video.VideoPlayerActivity"
+ } else {
+ null
+ },
+ action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ "org.videolan.vlc.player.result"
+ } else {
+ Intent.ACTION_VIEW
+ }
+) {
+ override val oneSource = false
+
+ // https://wiki.videolan.org/Android_Player_Intents/
+ override fun putExtra(
+ activity: Activity,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+
+ makeTempM3U8Intent(activity, intent, result)
+
+ val position = getViewPos(video.id)?.position ?: 0L
+
+ intent.putExtra("from_start", false)
+ intent.putExtra("position", position)
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getLongExtra("extra_position", -1) ?: -1
+ val duration = intent?.getLongExtra("extra_duration", -1) ?: -1
+ Log.d("VLC", "Position: $position, Duration: $duration")
+ updateDurationAndPosition(position, duration)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
new file mode 100644
index 000000000..468b2e4e4
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
@@ -0,0 +1,61 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class WebVideoCastPackage: OpenInAppAction(
+ txt("Web Video Cast"),
+ "com.instantbits.cast.webvideo"
+) {
+
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun putExtra(
+ activity: Activity,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links[index ?: 0]
+
+ intent.apply {
+ setDataAndType(Uri.parse(link.url), "video/*")
+
+ val title = video.name ?: video.headerName
+
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ putExtra("title", title)
+ video.poster?.let { putExtra("poster", it) }
+ val headers = Bundle().apply {
+ if (link.referer.isNotBlank())
+ putString("Referer", link.referer)
+ putString("User-Agent", USER_AGENT)
+ for ((key, value) in link.headers) {
+ putString(key, value)
+ }
+ }
+ putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
+ putExtra("secure_uri", true)
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
index 1eaac5056..b6556cbfe 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
@@ -3,10 +3,15 @@ package com.lagradost.cloudstream3.ui
import android.os.Bundle
import android.util.Log
import android.view.Menu
-import android.view.View.*
-import android.widget.*
+import android.view.View.GONE
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
+import android.widget.AbsListView
+import android.widget.ArrayAdapter
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ListView
import androidx.appcompat.app.AlertDialog
-import androidx.media3.common.util.UnstableApi
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.kotlinModule
@@ -25,7 +30,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.sortUrls
-import com.lagradost.cloudstream3.ui.player.LoadType
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_CHROMECAST
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.result.ResultEpisode
@@ -298,7 +303,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val isSuccessful = safeApiCall {
generator.generateLinks(
- clearCache = false, type = LoadType.Chromecast,
+ clearCache = false,
+ allowedTypes = LOADTYPE_CHROMECAST,
callback = {
it.first?.let { link ->
currentLinks.add(link)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
index c7db7d045..7d3d18ca9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
@@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.SubtitleUtils.cleanDisplayName
import com.lagradost.cloudstream3.utils.SubtitleUtils.isMatchingSubtitle
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadFileInfoAndUpdateSettings
@@ -57,10 +58,11 @@ class DownloadFileGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
val meta = episodes[currentIndex + offset]
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
index ec485f1c8..794dd762d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.player
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
class ExtractorLinkGenerator(
private val links: List,
@@ -37,15 +38,15 @@ class ExtractorLinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
subtitles.forEach(subtitleCallback)
- val allowedTypes = type.toSet()
links.forEach {
- if(allowedTypes.contains(it.type)) {
+ if(sourceTypes.contains(it.type)) {
callback.invoke(it to null)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
index 6b8e6ea88..89ce7974f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
@@ -3,45 +3,31 @@ package com.lagradost.cloudstream3.ui.player
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
-enum class LoadType {
- Unknown,
- InApp,
- InAppDownload,
- ExternalApp,
- Browser,
- Chromecast,
- Fcast
-}
+val LOADTYPE_INAPP = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_INAPP_DOWNLOAD = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_CHROMECAST = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_FCAST = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_ALL = ExtractorLinkType.entries.toSet()
-fun LoadType.toSet() : Set {
- return when(this) {
- LoadType.InApp -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.Browser -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.InAppDownload -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.M3U8
- )
- LoadType.ExternalApp, LoadType.Unknown -> ExtractorLinkType.entries.toSet()
- LoadType.Chromecast -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.Fcast -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- }
-}
interface IGenerator {
val hasCache: Boolean
@@ -60,9 +46,10 @@ interface IGenerator {
/* not safe, must use try catch */
suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
offset: Int = 0,
+ isCasting: Boolean = false
): Boolean
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
index 20feae413..109e3137b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
@@ -4,6 +4,7 @@ import android.net.Uri
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
@@ -69,10 +70,11 @@ class LinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
links.amap { link ->
if (!extract || !loadExtractor(link.url, referer, {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
index 122eaa975..67cd9de6d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
@@ -15,6 +15,7 @@ import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.EpisodeSkip
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@@ -94,7 +95,7 @@ class PlayerGeneratorViewModel : ViewModel() {
if (generator?.hasCache == true && generator?.hasNext() == true) {
safeApiCall {
generator?.generateLinks(
- type = LoadType.InApp,
+ sourceTypes = LOADTYPE_INAPP,
clearCache = false,
callback = {},
subtitleCallback = {},
@@ -173,7 +174,7 @@ class PlayerGeneratorViewModel : ViewModel() {
}
}
- fun loadLinks(type: LoadType = LoadType.InApp) {
+ fun loadLinks(sourceTypes: Set = LOADTYPE_INAPP) {
Log.i(TAG, "loadLinks")
currentJob?.cancel()
@@ -188,7 +189,7 @@ class PlayerGeneratorViewModel : ViewModel() {
// load more data
_loadingLinks.postValue(Resource.Loading())
val loadingState = safeApiCall {
- generator?.generateLinks(type = type, clearCache = forceClearCache, callback = {
+ generator?.generateLinks(sourceTypes = sourceTypes, clearCache = forceClearCache, callback = {
currentLinks.add(it)
// Clone to prevent ConcurrentModificationException
normalSafeApiCall {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
index 588afbb50..b97ca155b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
@@ -1,13 +1,13 @@
package com.lagradost.cloudstream3.ui.player
import android.util.Log
-import androidx.media3.common.util.UnstableApi
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import kotlin.math.max
import kotlin.math.min
@@ -75,12 +75,12 @@ class RepoLinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ allowedTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean,
): Boolean {
- val allowedTypes = type.toSet()
val index = currentIndex
val current = episodes.getOrNull(index + offset) ?: return false
@@ -123,7 +123,7 @@ class RepoLinkGenerator(
val result = APIRepository(
getApiFromNameNull(current.apiName) ?: throw Exception("This provider does not exist")
).loadLinks(current.data,
- isCasting = LoadType.Chromecast == type,
+ isCasting = isCasting,
subtitleCallback = { file ->
val correctFile = PlayerSubtitleHelper.getSubtitleData(file)
if (correctFile.url.isNotEmpty() && !currentSubsUrls.contains(correctFile.url)) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 99fc65f81..bbb835e1c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -2,16 +2,11 @@ package com.lagradost.cloudstream3.ui.result
import android.app.Activity
import android.content.*
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
import android.text.format.Formatter.formatFileSize
import android.util.Log
import android.widget.Toast
import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog
-import androidx.core.content.FileProvider
-import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -30,17 +25,6 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
import com.lagradost.cloudstream3.LoadResponse.Companion.readIdFromString
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO
-import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO_CAST_PACKAGE
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.*
@@ -48,20 +32,22 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
-import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.IGenerator
-import com.lagradost.cloudstream3.ui.player.LoadType
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_ALL
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_CHROMECAST
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_FCAST
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP_DOWNLOAD
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppContextUtils.getNameFull
-import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
import com.lagradost.cloudstream3.utils.AppContextUtils.isConnectedToChromecast
import com.lagradost.cloudstream3.utils.AppContextUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs
@@ -70,6 +56,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
@@ -102,7 +89,6 @@ import com.lagradost.cloudstream3.utils.fcast.FcastSession
import com.lagradost.cloudstream3.utils.fcast.Opcode
import com.lagradost.cloudstream3.utils.fcast.PlayMessage
import kotlinx.coroutines.*
-import java.io.File
import java.util.concurrent.TimeUnit
/** This starts at 1 */
@@ -804,7 +790,7 @@ class ResultViewModel2 : ViewModel() {
val generator = RepoLinkGenerator(listOf(episode))
val currentLinks = mutableSetOf()
val currentSubs = mutableSetOf()
- generator.generateLinks(clearCache = false, LoadType.Chromecast, callback = {
+ generator.generateLinks(clearCache = false, allowedTypes = LOADTYPE_CHROMECAST, callback = {
it.first?.let { link ->
currentLinks.add(link)
}
@@ -955,7 +941,7 @@ class ResultViewModel2 : ViewModel() {
isVisible: Boolean = true
) {
if (activity == null) return
- loadLinks(result, isVisible = isVisible, LoadType.Chromecast) { data ->
+ loadLinks(result, isVisible = isVisible, sourceTypes = LOADTYPE_CHROMECAST) { data ->
startChromecast(activity, result, data.links, data.subs, 0)
}
}
@@ -1301,7 +1287,7 @@ class ResultViewModel2 : ViewModel() {
private fun loadLinks(
result: ResultEpisode,
isVisible: Boolean,
- type: LoadType,
+ sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
work: suspend (CoroutineScope.(LinkLoadingResult) -> Unit)
) {
@@ -1310,7 +1296,7 @@ class ResultViewModel2 : ViewModel() {
val links = loadLinks(
result,
isVisible = isVisible,
- type = type,
+ sourceTypes = sourceTypes,
clearCache = clearCache
)
if (!this.isActive) return@ioSafe
@@ -1321,11 +1307,11 @@ class ResultViewModel2 : ViewModel() {
private var currentLoadLinkJob: Job? = null
private fun acquireSingleLink(
result: ResultEpisode,
- type: LoadType,
+ sourceTypes: Set,
text: UiText,
callback: (Pair) -> Unit,
) {
- loadLinks(result, isVisible = true, type) { links ->
+ loadLinks(result, isVisible = true, sourceTypes) { links ->
// Could not find a better way to do this
val context = AcraApplication.context
postPopup(
@@ -1345,7 +1331,7 @@ class ResultViewModel2 : ViewModel() {
text: UiText,
callback: (Pair) -> Unit,
) {
- loadLinks(result, isVisible = true, type = LoadType.Unknown) { links ->
+ loadLinks(result, isVisible = true) { links ->
postPopup(
text,
links.subs.map { txt(it.name) })
@@ -1358,7 +1344,7 @@ class ResultViewModel2 : ViewModel() {
private suspend fun CoroutineScope.loadLinks(
result: ResultEpisode,
isVisible: Boolean,
- type: LoadType,
+ sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
): LinkLoadingResult {
val tempGenerator = RepoLinkGenerator(listOf(result))
@@ -1372,7 +1358,7 @@ class ResultViewModel2 : ViewModel() {
}
try {
updatePage()
- tempGenerator.generateLinks(clearCache, type, { (link, _) ->
+ tempGenerator.generateLinks(clearCache, sourceTypes, { (link, _) ->
if (link != null) {
links += link
updatePage()
@@ -1390,185 +1376,11 @@ class ResultViewModel2 : ViewModel() {
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
}
- private fun launchActivity(
- activity: Activity?,
- resumeApp: MainActivity.Companion.ResultResume,
- id: Int? = null,
- work: suspend (Intent.(Activity) -> Unit)
- ): Job? {
- val act = activity ?: return null
- return CoroutineScope(Dispatchers.IO).launch {
- try {
- resumeApp.launch(id) {
- work(act)
- }
- } catch (t: Throwable) {
- logError(t)
- main {
- if (t is ActivityNotFoundException) {
- showToast(txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
- } else {
- showToast(t.toString(), Toast.LENGTH_LONG)
- }
- }
- }
- }
- }
-
- private fun playInWebVideo(
- activity: Activity?,
- link: ExtractorLink,
- title: String?,
- posterUrl: String?,
- subtitles: List
- ) = launchActivity(activity, WEB_VIDEO) {
- setDataAndType(Uri.parse(link.url), "video/*")
-
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- title?.let { putExtra("title", title) }
- posterUrl?.let { putExtra("poster", posterUrl) }
- val headers = Bundle().apply {
- if (link.referer.isNotBlank())
- putString("Referer", link.referer)
- putString("User-Agent", USER_AGENT)
- for ((key, value) in link.headers) {
- putString(key, value)
- }
- }
- putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
- putExtra("secure_uri", true)
- }
-
- private fun playWithMpv(
- activity: Activity?,
- id: Int,
- link: ExtractorLink,
- subtitles: List,
- resume: Boolean = true,
- ) = launchActivity(activity, MPV, id) {
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", subtitles.map { it.name }.toTypedArray())
- putExtra("subs.filename", subtitles.map { it.name }.toTypedArray())
- setDataAndType(Uri.parse(link.url), "video/*")
- component = MPV_COMPONENT
- putExtra("secure_uri", true)
- putExtra("return_result", true)
- val position = getViewPos(id)?.position
- if (resume && position != null)
- putExtra("position", position.toInt())
- }
-
- private fun playWithMpvYtdl(
- activity: Activity?,
- id: Int,
- link: ExtractorLink,
- subtitles: List,
- resume: Boolean = true,
- ) = launchActivity(activity, MPV_YTDL, id) {
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", subtitles.map { it.name }.toTypedArray())
- putExtra("subs.filename", subtitles.map { it.name }.toTypedArray())
- setDataAndType(Uri.parse(link.url), "video/*")
- component = MPV_YTDL_COMPONENT
- putExtra("secure_uri", true)
- putExtra("return_result", true)
- val position = getViewPos(id)?.position
- if (resume && position != null)
- putExtra("position", position.toInt())
- }
-
- // https://wiki.videolan.org/Android_Player_Intents/
- private fun playWithVlc(
- activity: Activity?,
- data: LinkLoadingResult,
- id: Int,
- resume: Boolean = true,
- // if it is only a single link then resume works correctly
- singleFile: Boolean? = null
- ) = launchActivity(activity, VLC, id) { act ->
- addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-
- val outputDir = act.cacheDir
-
- if (singleFile ?: (data.links.size == 1)) {
- setDataAndType(data.links.first().url.toUri(), "video/*")
- } else {
- val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
-
- var text = "#EXTM3U"
-
- // With subtitles it doesn't work for no reason :(
-// for (sub in data.subs) {
-// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
-// }
- for (link in data.links) {
- text += "\n#EXTINF:, ${link.name}\n${link.url}"
- }
- outputFile.writeText(text)
-
- setDataAndType(
- FileProvider.getUriForFile(
- act,
- act.applicationContext.packageName + ".provider",
- outputFile
- ), "video/*"
- )
- }
-
- val position = if (resume) {
- getViewPos(id)?.position ?: 0L
- } else {
- 1L
- }
-
- // Component no longer safe to use in A13 for VLC
- // https://code.videolan.org/videolan/vlc-android/-/issues/2776
- // This will likely need to be updated once VLC fixes their documentation.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- component = VLC_COMPONENT
- }
-
- putExtra("from_start", !resume)
- putExtra("position", position)
- }
-
-
fun handleAction(click: EpisodeClickEvent) =
viewModelScope.launchSafe {
handleEpisodeClickEvent(click)
}
- data class ExternalApp(
- val packageString: String,
- val name: Int,
- val action: Int,
- )
-
- private val apps = listOf(
- ExternalApp(
- VLC_PACKAGE,
- R.string.player_settings_play_in_vlc,
- ACTION_PLAY_EPISODE_IN_VLC_PLAYER
- ), ExternalApp(
- WEB_VIDEO_CAST_PACKAGE,
- R.string.player_settings_play_in_web,
- ACTION_PLAY_EPISODE_IN_WEB_VIDEO
- ),
- ExternalApp(
- MPV_PACKAGE,
- R.string.player_settings_play_in_mpv,
- ACTION_PLAY_EPISODE_IN_MPV
- ),
- ExternalApp(
- MPV_YTDL_PACKAGE,
- R.string.player_settings_play_in_mpvytdl,
- ACTION_PLAY_EPISODE_IN_MPV_YTDL
- )
- )
-
fun releaseEpisodeSynopsis() {
_episodeSynopsis.postValue(null)
}
@@ -1578,8 +1390,6 @@ class ResultViewModel2 : ViewModel() {
ACTION_SHOW_OPTIONS -> {
val options = mutableListOf>()
- VideoClickActionHolder.makeOptionMap(activity, click.data)
-
if (activity?.isConnectedToChromecast() == true) {
options.addAll(
listOf(
@@ -1597,20 +1407,8 @@ class ResultViewModel2 : ViewModel() {
options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER)
- for (app in apps) {
- if (activity?.isAppInstalled(app.packageString) == true) {
- options.add(
- txt(
- R.string.episode_action_play_in_format,
- txt(app.name)
- ) to app.action
- )
- }
- }
-
options.addAll(
listOf(
- txt(R.string.episode_action_play_in_browser) to ACTION_PLAY_EPISODE_IN_BROWSER,
txt(R.string.episode_action_copy_link) to ACTION_COPY_LINK,
txt(R.string.episode_action_auto_download) to ACTION_DOWNLOAD_EPISODE,
txt(R.string.episode_action_download_mirror) to ACTION_DOWNLOAD_MIRROR,
@@ -1724,7 +1522,7 @@ class ResultViewModel2 : ViewModel() {
val response = currentResponse ?: return
acquireSingleLink(
click.data,
- LoadType.InAppDownload,
+ LOADTYPE_INAPP_DOWNLOAD,
txt(R.string.episode_action_download_mirror)
) { (result, index) ->
ioSafe {
@@ -1754,7 +1552,7 @@ class ResultViewModel2 : ViewModel() {
loadLinks(
click.data,
isVisible = false,
- type = LoadType.InApp,
+ LOADTYPE_INAPP,
clearCache = true
)
}
@@ -1767,7 +1565,7 @@ class ResultViewModel2 : ViewModel() {
ACTION_CHROME_CAST_MIRROR -> {
acquireSingleLink(
click.data,
- LoadType.Chromecast,
+ LOADTYPE_CHROMECAST,
txt(R.string.episode_action_chromecast_mirror)
) { (result, index) ->
startChromecast(activity, click.data, result.links, result.subs, index)
@@ -1784,7 +1582,7 @@ class ResultViewModel2 : ViewModel() {
acquireSingleLink(
click.data,
- LoadType.Fcast,
+ LOADTYPE_FCAST,
txt(R.string.episode_action_cast_mirror)
) { (result, index) ->
val host = device?.host ?: return@acquireSingleLink
@@ -1807,24 +1605,10 @@ class ResultViewModel2 : ViewModel() {
}
}
- ACTION_PLAY_EPISODE_IN_BROWSER -> acquireSingleLink(
- click.data,
- LoadType.Browser,
- txt(R.string.episode_action_play_in_browser)
- ) { (result, index) ->
- try {
- val i = Intent(Intent.ACTION_VIEW)
- i.data = Uri.parse(result.links[index].url)
- activity?.startActivity(i)
- } catch (e: Exception) {
- logError(e)
- }
- }
-
ACTION_COPY_LINK -> {
acquireSingleLink(
click.data,
- LoadType.ExternalApp,
+ LOADTYPE_ALL,
txt(R.string.episode_action_copy_link)
) { (result, index) ->
val link = result.links[index]
@@ -1836,70 +1620,6 @@ class ResultViewModel2 : ViewModel() {
startChromecast(activity, click.data)
}
- ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
- loadLinks(click.data, isVisible = true, LoadType.ExternalApp) { links ->
- if (links.links.isEmpty()) {
- showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT)
- return@loadLinks
- }
-
- playWithVlc(
- activity,
- links,
- click.data.id
- )
- }
- }
-
- ACTION_PLAY_EPISODE_IN_WEB_VIDEO -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_web)
- )
- ) { (result, index) ->
- playInWebVideo(
- activity,
- result.links[index],
- click.data.name ?: click.data.headerName,
- click.data.poster,
- result.subs
- )
- }
-
- ACTION_PLAY_EPISODE_IN_MPV -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_mpv)
- )
- ) { (result, index) ->
- playWithMpv(
- activity,
- click.data.id,
- result.links[index],
- result.subs
- )
- }
-
- ACTION_PLAY_EPISODE_IN_MPV_YTDL -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_mpvytdl)
- )
- ) { (result, index) ->
- playWithMpvYtdl(
- activity,
- click.data.id,
- result.links[index],
- result.subs
- )
- }
-
ACTION_PLAY_EPISODE_IN_PLAYER -> {
val data = currentResponse?.syncData?.toList() ?: emptyList()
val list =
@@ -1915,7 +1635,7 @@ class ResultViewModel2 : ViewModel() {
if (currentResponse?.type == TvType.CustomMedia) {
generator?.generateLinks(
clearCache = true,
- LoadType.Unknown,
+ LOADTYPE_ALL,
callback = {},
subtitleCallback = {})
} else {
@@ -1945,20 +1665,12 @@ class ResultViewModel2 : ViewModel() {
else -> {
val action = VideoClickActionHolder.getActionById(click.action) ?: return
+ activity?.setKey("last_click_action", action.uniqueId())
// TODO: use action.sourceTypes
if (action.oneSource) {
- loadLinks(click.data, isVisible = true, LoadType.ExternalApp) { links ->
- action.runAction(
- activity,
- click.data,
- links,
- null
- )
- }
- } else {
acquireSingleLink(
click.data,
- LoadType.ExternalApp,
+ action.sourceTypes,
action.name
) { (result, index) ->
action.runAction(
@@ -1968,6 +1680,15 @@ class ResultViewModel2 : ViewModel() {
index
)
}
+ } else {
+ loadLinks(click.data, isVisible = true, action.sourceTypes) { links ->
+ action.runAction(
+ activity,
+ click.data,
+ links,
+ null
+ )
+ }
}
}
}
@@ -2077,7 +1798,7 @@ class ResultViewModel2 : ViewModel() {
isResponseRequired = false
)
if (map.isNullOrEmpty()) return@argamap
- updateEpisodes = DubStatus.values().map { dubStatus ->
+ updateEpisodes = DubStatus.entries.map { dubStatus ->
val current =
this.episodes[dubStatus]?.mapIndexed { index, episode ->
episode.apply {
From 6f76352cbea21312c02f0306e996ef470f7a2ebd Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Tue, 10 Sep 2024 21:17:36 +0200
Subject: [PATCH 003/956] move all possible actions to new api, handle some
todos
---
.../lagradost/cloudstream3/MainActivity.kt | 12 +--
.../cloudstream3/actions/OpenInAppAction.kt | 46 ++++++-----
.../cloudstream3/actions/VideoClickAction.kt | 35 ++++++--
.../cloudstream3/actions/temp/MpvKtPackage.kt | 14 ++--
.../cloudstream3/actions/temp/MpvPackage.kt | 22 ++---
.../actions/temp/PlayInBrowserAction.kt | 14 ++--
.../cloudstream3/actions/temp/TestAction.kt | 38 ++-------
.../cloudstream3/actions/temp/VlcPackage.kt | 10 ++-
.../actions/temp/WebVideoCastPackage.kt | 9 +-
.../actions/temp/fcast/FcastAction.kt | 66 +++++++++++++++
.../temp}/fcast/FcastManager.kt | 2 +-
.../temp}/fcast/FcastSession.kt | 2 +-
.../{utils => actions/temp}/fcast/Packets.kt | 2 +-
.../cloudstream3/ui/ControllerActivity.kt | 3 +-
.../cloudstream3/ui/player/IGenerator.kt | 6 --
.../cloudstream3/ui/result/EpisodeAdapter.kt | 25 +-----
.../ui/result/ResultViewModel2.kt | 82 ++++++-------------
.../ui/settings/SettingsPlayer.kt | 18 ++--
app/src/main/res/values/strings.xml | 2 +-
app/src/main/res/xml/settings_player.xml | 2 +-
20 files changed, 214 insertions(+), 196 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/FcastManager.kt (98%)
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/FcastSession.kt (96%)
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/Packets.kt (95%)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 0625f572e..fa54545cf 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -173,7 +173,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
-import com.lagradost.cloudstream3.utils.fcast.FcastManager
+import com.lagradost.cloudstream3.actions.temp.fcast.FcastManager
import com.lagradost.safefile.SafeFile
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -186,16 +186,6 @@ import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.system.exitProcess
-//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
-//https://wiki.videolan.org/Android_Player_Intents/
-
-//https://github.com/mpv-android/mpv-android/blob/0eb3cdc6f1632636b9c30d52ec50e4b017661980/app/src/main/java/is/xyz/mpv/MPVActivity.kt#L904
-//https://mpv-android.github.io/mpv-android/intent.html
-
-// https://www.webvideocaster.com/integrations
-
-//https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225
-
class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback {
companion object {
var activityResultLauncher: ActivityResultLauncher? = null
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 534102fea..45e61f2a8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.actions
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.widget.Toast
import androidx.core.content.FileProvider
@@ -36,9 +37,10 @@ fun updateDurationAndPosition(position: Long, duration: Long) {
* Util method that may be helpful for creating intents for apps that support m3u8 files.
* All sources are written to a temporary m3u8 file, which is then sent to the app.
*/
-fun makeTempM3U8Intent(activity: Activity,
- intent: Intent,
- result: LinkLoadingResult) {
+fun makeTempM3U8Intent(
+ context: Context,
+ intent: Intent,
+ result: LinkLoadingResult) {
intent.apply {
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
@@ -46,33 +48,35 @@ fun makeTempM3U8Intent(activity: Activity,
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
- val outputDir = activity.cacheDir
+ val outputDir = context.cacheDir
if (result.links.size == 1) {
intent.setDataAndType(result.links.first().url.toUri(), "video/*")
} else {
val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
- var text = "#EXTM3U"
+ var text = "#EXTM3U\n#EXT-X-VERSION:3"
- //With subtitles it doesn't work for no reason :(
+ result.links.forEachIndexed { index, link ->
+ text += "\n#EXTINF:$index,${link.name}\n${link.url}"
+ }
+
+ //With subtitles it doesn't work for no reason :(
/*for (sub in result.subs) {
val normalizedName = sub.name.replace("[^a-zA-Z0-9 ]".toRegex(), "")
- val language = fromLanguageToTwoLetters(sub.name, true) ?: "und"
- text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${language}\",URI=\"${sub.url}\""
+ text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.languageCode}\",URI=\"${sub.url}\""
}*/
- for (link in result.links) {
- text += "\n#EXTINF:, ${link.name}\n${link.url}"
- }
+ text += "\n#EXT-X-ENDLIST"
+
outputFile.writeText(text)
intent.setDataAndType(
FileProvider.getUriForFile(
- activity,
- activity.applicationContext.packageName + ".provider",
+ context,
+ context.applicationContext.packageName + ".provider",
outputFile
- ), "video/*"
+ ), "application/x-mpegURL"
)
}
}
@@ -86,21 +90,23 @@ abstract class OpenInAppAction(
override val name: UiText
get() = txt(R.string.episode_action_play_in_format, appName)
- override fun shouldShow(activity: Activity?, video: ResultEpisode) = activity?.isAppInstalled(packageName) == true
+ override val isPlayer = true
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = context?.isAppInstalled(packageName) == true
override fun runAction(
- activity: Activity?,
+ context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
- if (activity == null) return
+ if (context == null) return
val intent = Intent(action)
intent.setPackage(packageName)
if (intentClass != null) {
intent.component = ComponentName(packageName, intentClass)
}
- putExtra(activity, intent, video, result, index)
+ putExtra(context, intent, video, result, index)
setKey("last_opened_id", video.id)
try {
CoroutineScope(Dispatchers.IO).launch {
@@ -122,11 +128,11 @@ abstract class OpenInAppAction(
* Before intent is sent, this function is called to put extra data into the intent.
* @see VideoClickAction.runAction
* */
- abstract fun putExtra(activity: Activity, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+ abstract fun putExtra(context: Context, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
/**
* This function is called when the app is opened again after the intent was sent.
- * You can use it to for example read duration and position.
+ * You can use it to for example update duration and position.
* @see updateDurationAndPosition
*/
abstract fun onResult(activity: Activity, intent: Intent?)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index 75d3747ae..a19553901 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -1,6 +1,8 @@
package com.lagradost.cloudstream3.actions
import android.app.Activity
+import android.content.Context
+import com.lagradost.api.Log
import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
import com.lagradost.cloudstream3.actions.temp.MpvKtPreviewPackage
import com.lagradost.cloudstream3.actions.temp.MpvPackage
@@ -9,24 +11,31 @@ import com.lagradost.cloudstream3.actions.temp.PlayInBrowserAction
import com.lagradost.cloudstream3.actions.temp.TestAction
import com.lagradost.cloudstream3.actions.temp.VlcPackage
import com.lagradost.cloudstream3.actions.temp.WebVideoCastPackage
+import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLinkType
+import kotlin.reflect.jvm.jvmName
object VideoClickActionHolder {
val allVideoClickActions = threadSafeListOf(
PlayInBrowserAction(), VlcPackage(), TestAction(),
MpvPackage(), MpvYTDLPackage(),
- WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage()
+ WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage(),
+ FcastAction()
)
+ init {
+ Log.d("VideoClickActionHolder", "allVideoClickActions: ${allVideoClickActions.map { it.uniqueId() }}")
+ }
+
private const val ACTION_ID_OFFSET = 1000
fun makeOptionMap(activity: Activity?, video: ResultEpisode) = allVideoClickActions
// We need to have index before filtering
- .mapIndexed { index, it -> it to index + ACTION_ID_OFFSET }
+ .mapIndexed { id, it -> it to id + ACTION_ID_OFFSET }
.filter { it.first.shouldShow(activity, video) }
.map { it.first.name to it.second }
@@ -34,6 +43,16 @@ object VideoClickActionHolder {
fun getActionById(id: Int): VideoClickAction? = allVideoClickActions.getOrNull(id - ACTION_ID_OFFSET)
fun getByUniqueId(uniqueId: String): VideoClickAction? = allVideoClickActions.firstOrNull { it.uniqueId() == uniqueId }
+
+ fun uniqueIdToId(uniqueId: String?): Int? {
+ if (uniqueId == null) return null
+ return allVideoClickActions
+ .mapIndexed { id, it -> it to id + ACTION_ID_OFFSET }
+ .firstOrNull { it.first.uniqueId() == uniqueId }
+ ?.second
+ }
+
+ fun getPlayers(activity: Activity? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShow(activity, null) }
}
abstract class VideoClickAction {
@@ -42,8 +61,8 @@ abstract class VideoClickAction {
/** if true, the app will show dialog to select source - result.links[index] */
open val oneSource : Boolean = false
- /** if true, this action could be selected as default one press action in settings */
- open val canBeDefault: Boolean = false
+ /** if true, this action could be selected as default player (one press action) in settings */
+ open val isPlayer: Boolean = false
/** Which type of sources this action can handle. */
open val sourceTypes: Set = ExtractorLinkType.entries.toSet()
@@ -51,16 +70,16 @@ abstract class VideoClickAction {
/** Determines which plugin a given provider is from. This is the full path to the plugin. */
var sourcePlugin: String? = null
- fun uniqueId() = "$sourcePlugin:${this::class.simpleName}"
+ fun uniqueId() = "$sourcePlugin:${this::class.jvmName}"
- abstract fun shouldShow(activity: Activity?, video: ResultEpisode): Boolean
+ abstract fun shouldShow(context: Context?, video: ResultEpisode?): Boolean
/**
* This function is called when the action is clicked.
- * @param activity The current activity
+ * @param context The current activity
* @param video The episode/movie that was clicked
* @param result The result of the link loading, contains video & subtitle links
* @param index if oneSource is true, this is the index of the selected source
*/
- abstract fun runAction(activity: Activity?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+ abstract fun runAction(context: Context?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
index 57f564d24..f5ded49b8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.actions.temp
import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
@@ -34,27 +35,28 @@ open class MpvKtPackage(
)
override fun putExtra(
- activity: Activity,
+ context: Context,
intent: Intent,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
- val link = result.links[index ?: 0]
+ val link = result.links.getOrNull(index ?: 0) ?: return
intent.apply {
putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", result.subs.map { it.name }.toTypedArray())
- putExtra("subs.filename", result.subs.map { it.name }.toTypedArray())
setDataAndType(Uri.parse(link.url), "video/*")
+
// m3u8 plays, but changing sources feature is not available
// makeTempM3U8Intent(activity, this, result)
- putExtra("secure_uri", true)
- putExtra("return_result", true)
+
//putExtra("headers", link.headers.flatMap { listOf(it.key, it.value) }.toTypedArray())
+
val position = getViewPos(video.id)?.position
if (position != null)
putExtra("position", position.toInt())
+
+ putExtra("secure_uri", true)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
index 7ff05a3f1..4c66d0450 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.actions.temp
import android.app.Activity
+import android.content.Context
import android.content.Intent
import androidx.core.net.toUri
import com.lagradost.api.Log
@@ -13,6 +14,9 @@ import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLinkType
+// https://github.com/mpv-android/mpv-android/blob/0eb3cdc6f1632636b9c30d52ec50e4b017661980/app/src/main/java/is/xyz/mpv/MPVActivity.kt#L904
+// https://mpv-android.github.io/mpv-android/intent.html
+
class MpvYTDLPackage : MpvPackage("MPV YTDL", "is.xyz.mpv.ytdl") {
override val sourceTypes = setOf(
ExtractorLinkType.VIDEO,
@@ -26,14 +30,9 @@ open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv
packageName,
"is.xyz.mpv.MPVActivity"
) {
- override val sourceTypes = setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
override fun putExtra(
- activity: Activity,
+ context: Context,
intent: Intent,
video: ResultEpisode,
result: LinkLoadingResult,
@@ -41,14 +40,15 @@ open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv
) {
intent.apply {
putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", result.subs.map { it.name }.toTypedArray())
- putExtra("subs.filename", result.subs.map { it.name }.toTypedArray())
- makeTempM3U8Intent(activity, this, result)
- putExtra("secure_uri", true)
- putExtra("return_result", true)
+ putExtra("title", video.name)
+
+ makeTempM3U8Intent(context, this, result)
+
val position = getViewPos(video.id)?.position
if (position != null)
putExtra("position", position.toInt())
+
+ putExtra("secure_uri", true)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
index 6d6f78f51..39b689398 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -1,6 +1,6 @@
package com.lagradost.cloudstream3.actions.temp
-import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.net.Uri
import com.lagradost.cloudstream3.actions.VideoClickAction
@@ -15,25 +15,27 @@ class PlayInBrowserAction: VideoClickAction() {
override val oneSource = true
+ override val isPlayer = true
+
override val sourceTypes: Set = setOf(
ExtractorLinkType.VIDEO,
ExtractorLinkType.DASH,
ExtractorLinkType.M3U8
)
- override fun shouldShow(activity: Activity?, video: ResultEpisode) = true
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
override fun runAction(
- activity: Activity?,
+ context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
- if (index == null) return
+ val link = result.links.getOrNull(index ?: 0) ?: return
try {
val i = Intent(Intent.ACTION_VIEW)
- i.data = Uri.parse(result.links[index].url)
- activity?.startActivity(i)
+ i.data = Uri.parse(link.url)
+ context?.startActivity(i)
} catch (e: Exception) {
logError(e)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
index 3c7464a05..c825202a1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
@@ -1,47 +1,27 @@
package com.lagradost.cloudstream3.actions.temp
-import android.app.Activity
+import android.content.Context
+import android.content.Intent
import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
-import com.lagradost.cloudstream3.utils.AppUtils.toJson
class TestAction: VideoClickAction() {
override val name = txt("Test action")
- override fun shouldShow(activity: Activity?, video: ResultEpisode): Boolean {
- return true
- }
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
override fun runAction(
- activity: Activity?,
+ context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
- // Show dialog box
- val text = """
-Result links:
-${result.links.joinToString("\n") { if (it.url.length > 50) it.url.take(25) + "..." + it.url.takeLast(25) else it.url }}
-Result subs:
-${result.subs.joinToString("\n") { if (it.url.length > 50) it.url.take(25) + "..." + it.url.takeLast(25) else it.url }}
-Video:
-${video.toJson()}
- """.trim()
-
-
- activity?.runOnUiThread {
- val dialog = android.app.AlertDialog.Builder(activity)
- dialog.apply {
- setTitle("Action: ${video.name}")
- setMessage(text)
- setPositiveButton("OK") { dialog, _ ->
- dialog.dismiss()
- }
- }
- dialog.show()
- }
-
+ if (context == null) return
+ val i = Intent(Intent.ACTION_VIEW)
+ makeTempM3U8Intent(context, i, result)
+ context.startActivity(i)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
index 7f3b34d01..ec824d4eb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.actions.temp
import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.os.Build
import com.lagradost.api.Log
@@ -12,6 +13,9 @@ import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+// https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
+// https://wiki.videolan.org/Android_Player_Intents/
+
class VlcPackage: OpenInAppAction(
appName = txt("VLC"),
packageName = "org.videolan.vlc",
@@ -28,21 +32,21 @@ class VlcPackage: OpenInAppAction(
) {
override val oneSource = false
- // https://wiki.videolan.org/Android_Player_Intents/
override fun putExtra(
- activity: Activity,
+ context: Context,
intent: Intent,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
- makeTempM3U8Intent(activity, intent, result)
+ makeTempM3U8Intent(context, intent, result)
val position = getViewPos(video.id)?.position ?: 0L
intent.putExtra("from_start", false)
intent.putExtra("position", position)
+ intent.putExtra("secure_uri", true)
}
override fun onResult(activity: Activity, intent: Intent?) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
index 468b2e4e4..f8419f63c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.actions.temp
import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@@ -12,6 +13,8 @@ import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.ExtractorLinkType
+// https://www.webvideocaster.com/integrations
+
class WebVideoCastPackage: OpenInAppAction(
txt("Web Video Cast"),
"com.instantbits.cast.webvideo"
@@ -26,7 +29,7 @@ class WebVideoCastPackage: OpenInAppAction(
)
override fun putExtra(
- activity: Activity,
+ context: Context,
intent: Intent,
video: ResultEpisode,
result: LinkLoadingResult,
@@ -55,7 +58,5 @@ class WebVideoCastPackage: OpenInAppAction(
}
}
- override fun onResult(activity: Activity, intent: Intent?) {
-
- }
+ override fun onResult(activity: Activity, intent: Intent?) = Unit
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
new file mode 100644
index 000000000..f2f7621e8
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -0,0 +1,66 @@
+package com.lagradost.cloudstream3.actions.temp.fcast
+
+import android.content.Context
+import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
+
+class FcastAction: VideoClickAction() {
+ override val name = txt("Fcast")
+
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = FcastManager.currentDevices.isNotEmpty()
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links.getOrNull(index ?: 0) ?: return
+ val devices = FcastManager.currentDevices.toList()
+ context?.getActivity()?.showBottomDialog(
+ devices.map { it.name },
+ -1,
+ "Select device",
+ false,
+ {}) {
+ val position = getViewPos(video.id)?.position
+ castTo(devices.getOrNull(it), link, position)
+ }
+ }
+
+
+ private fun castTo(device: PublicDeviceInfo?, link: ExtractorLink, position: Long?) {
+ val host = device?.host ?: return
+
+ FcastSession(host).use { session ->
+ session.sendMessage(
+ Opcode.Play,
+ PlayMessage(
+ "application/vnd.apple.mpegurl",
+ link.url,
+ time = position?.let { it / 1000.0 },
+ headers = mapOf(
+ "referer" to link.referer,
+ "user-agent" to USER_AGENT
+ ) + link.headers
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
similarity index 98%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
index e7c36a872..78682ca1c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
import android.content.Context
import android.net.nsd.NsdManager
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
similarity index 96%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
index 1f33bca43..326d11191 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
import android.util.Log
import androidx.annotation.WorkerThread
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
similarity index 95%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
index 61c00d6ed..26f5cec53 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
// See https://gitlab.com/futo-org/fcast/-/wikis/Protocol-version-1
enum class Opcode(val value: Byte) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
index b6556cbfe..4b5d680c3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
@@ -311,7 +311,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
}
}, subtitleCallback = {
currentSubs.add(it)
- })
+ },
+ isCasting = true)
}
val sortedLinks = sortUrls(currentLinks)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
index 89ce7974f..31cf0c70f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
@@ -20,12 +20,6 @@ val LOADTYPE_CHROMECAST = setOf(
ExtractorLinkType.M3U8
)
-val LOADTYPE_FCAST = setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
-)
-
val LOADTYPE_ALL = ExtractorLinkType.entries.toSet()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index a19adc4e8..0c754f513 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -11,6 +11,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.databinding.ResultEpisodeBinding
import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
@@ -34,10 +35,7 @@ import java.util.Locale
* Ids >= 1000 are reserved for VideoClickActions
* @see VideoClickActionHolder
*/
-
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
-const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2
-const val ACTION_PLAY_EPISODE_IN_BROWSER = 3
const val ACTION_CHROME_CAST_EPISODE = 4
const val ACTION_CHROME_CAST_MIRROR = 5
@@ -57,12 +55,7 @@ const val ACTION_SHOW_DESCRIPTION = 15
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
-const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
-const val ACTION_PLAY_EPISODE_IN_MPV = 17
-const val ACTION_PLAY_EPISODE_IN_MPV_YTDL = 20
-
const val ACTION_MARK_AS_WATCHED = 18
-const val ACTION_FCAST = 19
const val TV_EP_SIZE = 400
@@ -74,22 +67,10 @@ class EpisodeAdapter(
private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.Adapter() {
companion object {
- /**
- * @return ACTION_PLAY_EPISODE_IN_PLAYER, ACTION_PLAY_EPISODE_IN_BROWSER or ACTION_PLAY_EPISODE_IN_VLC_PLAYER depending on player settings.
- * See array.xml/player_pref_values
- **/
fun getPlayerAction(context: Context): Int {
-
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
- return when (settingsManager.getInt(context.getString(R.string.player_pref_key), 1)) {
- 1 -> ACTION_PLAY_EPISODE_IN_PLAYER
- 2 -> ACTION_PLAY_EPISODE_IN_VLC_PLAYER
- 3 -> ACTION_PLAY_EPISODE_IN_BROWSER
- 4 -> ACTION_PLAY_EPISODE_IN_WEB_VIDEO
- 5 -> ACTION_PLAY_EPISODE_IN_MPV
- 6 -> ACTION_PLAY_EPISODE_IN_MPV_YTDL
- else -> ACTION_PLAY_EPISODE_IN_PLAYER
- }
+ val playerPref = settingsManager.getString(context.getString(R.string.player_default_key), "")
+ return VideoClickActionHolder.uniqueIdToId(playerPref) ?: ACTION_PLAY_EPISODE_IN_PLAYER
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index bbb835e1c..a330d5638 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -39,7 +39,6 @@ import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.IGenerator
import com.lagradost.cloudstream3.ui.player.LOADTYPE_ALL
import com.lagradost.cloudstream3.ui.player.LOADTYPE_CHROMECAST
-import com.lagradost.cloudstream3.ui.player.LOADTYPE_FCAST
import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP
import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP_DOWNLOAD
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
@@ -84,10 +83,6 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
import com.lagradost.cloudstream3.utils.UIHelper.navigate
-import com.lagradost.cloudstream3.utils.fcast.FcastManager
-import com.lagradost.cloudstream3.utils.fcast.FcastSession
-import com.lagradost.cloudstream3.utils.fcast.Opcode
-import com.lagradost.cloudstream3.utils.fcast.PlayMessage
import kotlinx.coroutines.*
import java.util.concurrent.TimeUnit
@@ -790,7 +785,7 @@ class ResultViewModel2 : ViewModel() {
val generator = RepoLinkGenerator(listOf(episode))
val currentLinks = mutableSetOf()
val currentSubs = mutableSetOf()
- generator.generateLinks(clearCache = false, allowedTypes = LOADTYPE_CHROMECAST, callback = {
+ generator.generateLinks(clearCache = false, allowedTypes = LOADTYPE_INAPP_DOWNLOAD, callback = {
it.first?.let { link ->
currentLinks.add(link)
}
@@ -941,7 +936,7 @@ class ResultViewModel2 : ViewModel() {
isVisible: Boolean = true
) {
if (activity == null) return
- loadLinks(result, isVisible = isVisible, sourceTypes = LOADTYPE_CHROMECAST) { data ->
+ loadLinks(result, isVisible = isVisible, sourceTypes = LOADTYPE_CHROMECAST, isCasting = true) { data ->
startChromecast(activity, result, data.links, data.subs, 0)
}
}
@@ -1251,7 +1246,7 @@ class ResultViewModel2 : ViewModel() {
_loadedLinks.postValue(null)
}
- private fun postPopup(text: UiText, options: List, callback: suspend (Int?) -> Unit) {
+ fun postPopup(text: UiText, options: List, callback: suspend (Int?) -> Unit) {
_selectPopup.postValue(
SelectPopup.SelectText(
text,
@@ -1289,6 +1284,7 @@ class ResultViewModel2 : ViewModel() {
isVisible: Boolean,
sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
+ isCasting: Boolean = false,
work: suspend (CoroutineScope.(LinkLoadingResult) -> Unit)
) {
currentLoadLinkJob?.cancel()
@@ -1297,7 +1293,8 @@ class ResultViewModel2 : ViewModel() {
result,
isVisible = isVisible,
sourceTypes = sourceTypes,
- clearCache = clearCache
+ clearCache = clearCache,
+ isCasting = isCasting
)
if (!this.isActive) return@ioSafe
work(links)
@@ -1309,9 +1306,10 @@ class ResultViewModel2 : ViewModel() {
result: ResultEpisode,
sourceTypes: Set,
text: UiText,
- callback: (Pair) -> Unit,
+ isCasting: Boolean = false,
+ callback: (Pair) -> Unit
) {
- loadLinks(result, isVisible = true, sourceTypes) { links ->
+ loadLinks(result, isVisible = true, sourceTypes, isCasting = isCasting) { links ->
// Could not find a better way to do this
val context = AcraApplication.context
postPopup(
@@ -1346,6 +1344,7 @@ class ResultViewModel2 : ViewModel() {
isVisible: Boolean,
sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
+ isCasting: Boolean = false
): LinkLoadingResult {
val tempGenerator = RepoLinkGenerator(listOf(result))
@@ -1358,15 +1357,19 @@ class ResultViewModel2 : ViewModel() {
}
try {
updatePage()
- tempGenerator.generateLinks(clearCache, sourceTypes, { (link, _) ->
- if (link != null) {
- links += link
- updatePage()
- }
- }, { sub ->
+ tempGenerator.generateLinks(clearCache,
+ allowedTypes = sourceTypes,
+ callback = { (link, _) ->
+ if (link != null) {
+ links += link
+ updatePage()
+ }
+ },
+ subtitleCallback = { sub ->
subs += sub
updatePage()
- })
+ },
+ isCasting = isCasting)
} catch (e: Exception) {
logError(e)
} finally {
@@ -1399,12 +1402,6 @@ class ResultViewModel2 : ViewModel() {
)
}
- if (FcastManager.currentDevices.isNotEmpty()) {
- options.add(
- txt(R.string.player_settings_play_in_fcast) to ACTION_FCAST
- )
- }
-
options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER)
options.addAll(
@@ -1566,45 +1563,13 @@ class ResultViewModel2 : ViewModel() {
acquireSingleLink(
click.data,
LOADTYPE_CHROMECAST,
- txt(R.string.episode_action_chromecast_mirror)
+ txt(R.string.episode_action_chromecast_mirror),
+ isCasting = true
) { (result, index) ->
startChromecast(activity, click.data, result.links, result.subs, index)
}
}
- ACTION_FCAST -> {
- val devices = FcastManager.currentDevices.toList()
- postPopup(
- txt(R.string.player_settings_select_cast_device),
- devices.map { txt(it.name) }) { index ->
- if (index == null) return@postPopup
- val device = devices.getOrNull(index)
-
- acquireSingleLink(
- click.data,
- LOADTYPE_FCAST,
- txt(R.string.episode_action_cast_mirror)
- ) { (result, index) ->
- val host = device?.host ?: return@acquireSingleLink
- val link = result.links.getOrNull(index) ?: return@acquireSingleLink
-
- FcastSession(host).use { session ->
- session.sendMessage(
- Opcode.Play,
- PlayMessage(
- link.type.getMimeType(),
- link.url,
- headers = mapOf(
- "referer" to link.referer,
- "user-agent" to USER_AGENT
- ) + link.headers
- )
- )
- }
- }
- }
- }
-
ACTION_COPY_LINK -> {
acquireSingleLink(
click.data,
@@ -1666,7 +1631,6 @@ class ResultViewModel2 : ViewModel() {
val action = VideoClickActionHolder.getActionById(click.action) ?: return
activity?.setKey("last_click_action", action.uniqueId())
- // TODO: use action.sourceTypes
if (action.oneSource) {
acquireSingleLink(
click.data,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
index 1753032ac..17580236f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
@@ -6,6 +6,7 @@ import android.view.View
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
@@ -155,10 +156,17 @@ class SettingsPlayer : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
- getPref(R.string.player_pref_key)?.setOnPreferenceClickListener {
- val prefNames = resources.getStringArray(R.array.player_pref_names)
- val prefValues = resources.getIntArray(R.array.player_pref_values)
- val current = settingsManager.getInt(getString(R.string.player_pref_key), 1)
+ getPref(R.string.player_default_key)?.setOnPreferenceClickListener {
+ val players = VideoClickActionHolder.getPlayers(activity)
+ val prefNames = buildList {
+ add(getString(R.string.player_settings_play_in_app))
+ addAll(players.map { it.name.asStringNull(activity) ?: it.javaClass.simpleName })
+ }
+ val prefValues = buildList {
+ add("")
+ addAll(players.map { it.uniqueId() })
+ }
+ val current = settingsManager.getString(getString(R.string.player_default_key), "") ?: ""
activity?.showBottomDialog(
prefNames.toList(),
@@ -166,7 +174,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
getString(R.string.player_pref),
true,
{}) {
- settingsManager.edit().putInt(getString(R.string.player_pref_key), prefValues[it]).apply()
+ settingsManager.edit().putString(getString(R.string.player_default_key), prefValues[it]).apply()
}
return@setOnPreferenceClickListener true
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index b2ec8a760..7c7553208 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,7 +17,7 @@
subtitle_settings_chromecast_key
quality_pref_key
quality_pref_mobile_data_key
- player_pref_key
+ player_default_key
prefer_limit_title_key
prefer_limit_title_rez_key
apk_installer_key
diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml
index 73f9bb5bf..a2575e315 100644
--- a/app/src/main/res/xml/settings_player.xml
+++ b/app/src/main/res/xml/settings_player.xml
@@ -22,7 +22,7 @@
From e288c83c3d748bfc1b09c5337fffeebd90d28f54 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Tue, 17 Sep 2024 23:37:59 +0200
Subject: [PATCH 004/956] remove unused strings, address comment
---
.../cloudstream3/actions/OpenInAppAction.kt | 11 ++---
.../cloudstream3/actions/VideoClickAction.kt | 6 ++-
.../actions/temp/CopyClipboardAction.kt | 27 ++++++++++++
.../actions/temp/PlayInBrowserAction.kt | 3 +-
.../temp/{TestAction.kt => ViewM3U8Action.kt} | 7 ++-
.../cloudstream3/actions/temp/VlcPackage.kt | 11 ++++-
.../actions/temp/fcast/FcastAction.kt | 7 +--
.../cloudstream3/ui/result/EpisodeAdapter.kt | 1 -
.../ui/result/ResultViewModel2.kt | 12 -----
app/src/main/res/values-ajp/strings.xml | 8 ----
app/src/main/res/values-ar/strings.xml | 8 ----
app/src/main/res/values-ars/strings.xml | 2 -
app/src/main/res/values-as/strings.xml | 8 ----
app/src/main/res/values-bg/strings.xml | 6 ---
app/src/main/res/values-bn/strings.xml | 2 -
app/src/main/res/values-bp/strings.xml | 8 ----
app/src/main/res/values-cs/strings.xml | 8 ----
app/src/main/res/values-de/strings.xml | 7 ---
app/src/main/res/values-el/strings.xml | 7 ---
app/src/main/res/values-es/array.xml | 26 -----------
app/src/main/res/values-es/strings.xml | 8 ----
app/src/main/res/values-fr/strings.xml | 8 +---
app/src/main/res/values-hi/strings.xml | 2 -
app/src/main/res/values-hr/strings.xml | 8 ----
app/src/main/res/values-hu/strings.xml | 6 ---
app/src/main/res/values-in/strings.xml | 7 ---
app/src/main/res/values-it/strings.xml | 8 ----
app/src/main/res/values-iw/strings.xml | 6 ---
app/src/main/res/values-ja/strings.xml | 3 --
app/src/main/res/values-ko/strings.xml | 7 ---
app/src/main/res/values-lt/strings.xml | 5 ---
app/src/main/res/values-lv/strings.xml | 6 ---
app/src/main/res/values-mk/strings.xml | 7 ---
app/src/main/res/values-ml/strings.xml | 2 -
app/src/main/res/values-ms/strings.xml | 7 ---
app/src/main/res/values-my/strings.xml | 6 ---
app/src/main/res/values-nl/strings.xml | 6 ---
app/src/main/res/values-nn/strings.xml | 2 -
app/src/main/res/values-no/strings.xml | 6 ---
app/src/main/res/values-or/strings.xml | 4 --
app/src/main/res/values-pl/array.xml | 26 -----------
app/src/main/res/values-pl/strings.xml | 8 ----
app/src/main/res/values-pt/strings.xml | 7 ---
app/src/main/res/values-qt/strings.xml | 7 ---
app/src/main/res/values-ro/strings.xml | 7 ---
app/src/main/res/values-ru/strings.xml | 7 ---
app/src/main/res/values-sk/strings.xml | 2 -
app/src/main/res/values-so/strings.xml | 6 ---
app/src/main/res/values-sv/strings.xml | 7 ---
app/src/main/res/values-ta/strings.xml | 7 ---
app/src/main/res/values-tl/strings.xml | 2 -
app/src/main/res/values-tr/array.xml | 42 ------------------
app/src/main/res/values-tr/strings.xml | 7 ---
app/src/main/res/values-uk/strings.xml | 8 ----
app/src/main/res/values-ur/strings.xml | 6 ---
app/src/main/res/values-vi/array.xml | 26 -----------
app/src/main/res/values-vi/strings.xml | 8 ----
app/src/main/res/values-zh-rTW/strings.xml | 7 ---
app/src/main/res/values-zh/strings.xml | 8 ----
app/src/main/res/values/array.xml | 44 -------------------
app/src/main/res/values/strings.xml | 8 ----
61 files changed, 56 insertions(+), 483 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
rename app/src/main/java/com/lagradost/cloudstream3/actions/temp/{TestAction.kt => ViewM3U8Action.kt} (80%)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 45e61f2a8..99c1ac38b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -20,7 +20,6 @@ import com.lagradost.cloudstream3.ui.result.ResultFragment
import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
-import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStoreHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -112,15 +111,11 @@ abstract class OpenInAppAction(
CoroutineScope(Dispatchers.IO).launch {
activityResultLauncher?.launch(intent)
}
+ } catch (_: ActivityNotFoundException) {
+ showToast(R.string.app_not_found_error, Toast.LENGTH_LONG)
} catch (t: Throwable) {
logError(t)
- main {
- if (t is ActivityNotFoundException) {
- showToast(txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
- } else {
- showToast(t.toString(), Toast.LENGTH_LONG)
- }
- }
+ showToast(t.toString(), Toast.LENGTH_LONG)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index a19553901..f66ed74d9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -3,12 +3,13 @@ package com.lagradost.cloudstream3.actions
import android.app.Activity
import android.content.Context
import com.lagradost.api.Log
+import com.lagradost.cloudstream3.actions.temp.CopyClipboardAction
import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
import com.lagradost.cloudstream3.actions.temp.MpvKtPreviewPackage
import com.lagradost.cloudstream3.actions.temp.MpvPackage
import com.lagradost.cloudstream3.actions.temp.MpvYTDLPackage
import com.lagradost.cloudstream3.actions.temp.PlayInBrowserAction
-import com.lagradost.cloudstream3.actions.temp.TestAction
+import com.lagradost.cloudstream3.actions.temp.ViewM3U8Action
import com.lagradost.cloudstream3.actions.temp.VlcPackage
import com.lagradost.cloudstream3.actions.temp.WebVideoCastPackage
import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
@@ -21,7 +22,8 @@ import kotlin.reflect.jvm.jvmName
object VideoClickActionHolder {
val allVideoClickActions = threadSafeListOf(
- PlayInBrowserAction(), VlcPackage(), TestAction(),
+ PlayInBrowserAction(), CopyClipboardAction(),
+ VlcPackage(), ViewM3U8Action(),
MpvPackage(), MpvYTDLPackage(),
WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage(),
FcastAction()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
new file mode 100644
index 000000000..e054b5ce2
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
@@ -0,0 +1,27 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.content.Context
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
+
+class CopyClipboardAction: VideoClickAction() {
+ override val name = txt("Copy to clipboard")
+
+ override val oneSource = true
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (index == null) return
+ val link = result.links.getOrNull(index) ?: return
+ clipboardHelper(txt(link.name), link.url)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
index 39b689398..de32bb4b3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.actions.temp
import android.content.Context
import android.content.Intent
import android.net.Uri
+import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
@@ -11,7 +12,7 @@ import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.ExtractorLinkType
class PlayInBrowserAction: VideoClickAction() {
- override val name = txt("Play in browser")
+ override val name = txt(R.string.episode_action_play_in_format, "Browser")
override val oneSource = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
similarity index 80%
rename from app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
index c825202a1..c14168e96 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/TestAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
@@ -2,14 +2,17 @@ package com.lagradost.cloudstream3.actions.temp
import android.content.Context
import android.content.Intent
+import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
-class TestAction: VideoClickAction() {
- override val name = txt("Test action")
+class ViewM3U8Action: VideoClickAction() {
+ override val name = txt(R.string.episode_action_play_in_format, "m3u8 player")
+
+ override val isPlayer = true
override fun shouldShow(context: Context?, video: ResultEpisode?) = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
index ec824d4eb..ecb37fdc6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -5,12 +5,14 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import com.lagradost.api.Log
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.actions.OpenInAppAction
import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.actions.updateDurationAndPosition
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
// https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
@@ -47,6 +49,14 @@ class VlcPackage: OpenInAppAction(
intent.putExtra("from_start", false)
intent.putExtra("position", position)
intent.putExtra("secure_uri", true)
+ intent.putExtra("title", video.name)
+
+ val subsLang = getKey(SUBTITLE_AUTO_SELECT_KEY) ?: "en"
+ result.subs.firstOrNull {
+ subsLang == it.languageCode
+ }?.let {
+ intent.putExtra("subtitles_location", it.url)
+ }
}
override fun onResult(activity: Activity, intent: Intent?) {
@@ -55,5 +65,4 @@ class VlcPackage: OpenInAppAction(
Log.d("VLC", "Position: $position, Duration: $duration")
updateDurationAndPosition(position, duration)
}
-
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
index f2f7621e8..c0f92e4df 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -2,6 +2,7 @@ package com.lagradost.cloudstream3.actions.temp.fcast
import android.content.Context
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
+import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
@@ -13,7 +14,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
class FcastAction: VideoClickAction() {
- override val name = txt("Fcast")
+ override val name = txt("Fcast to device")
override val oneSource = true
@@ -36,7 +37,7 @@ class FcastAction: VideoClickAction() {
context?.getActivity()?.showBottomDialog(
devices.map { it.name },
-1,
- "Select device",
+ txt(R.string.player_settings_select_cast_device).asString(context),
false,
{}) {
val position = getViewPos(video.id)?.position
@@ -52,7 +53,7 @@ class FcastAction: VideoClickAction() {
session.sendMessage(
Opcode.Play,
PlayMessage(
- "application/vnd.apple.mpegurl",
+ "video/*",
link.url,
time = position?.let { it / 1000.0 },
headers = mapOf(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 0c754f513..2dd8e2ab4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -44,7 +44,6 @@ const val ACTION_DOWNLOAD_EPISODE = 6
const val ACTION_DOWNLOAD_MIRROR = 7
const val ACTION_RELOAD_EPISODE = 8
-const val ACTION_COPY_LINK = 9
const val ACTION_SHOW_OPTIONS = 10
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index a330d5638..b5f83201e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -1406,7 +1406,6 @@ class ResultViewModel2 : ViewModel() {
options.addAll(
listOf(
- txt(R.string.episode_action_copy_link) to ACTION_COPY_LINK,
txt(R.string.episode_action_auto_download) to ACTION_DOWNLOAD_EPISODE,
txt(R.string.episode_action_download_mirror) to ACTION_DOWNLOAD_MIRROR,
txt(R.string.episode_action_download_subtitle) to ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR,
@@ -1570,17 +1569,6 @@ class ResultViewModel2 : ViewModel() {
}
}
- ACTION_COPY_LINK -> {
- acquireSingleLink(
- click.data,
- LOADTYPE_ALL,
- txt(R.string.episode_action_copy_link)
- ) { (result, index) ->
- val link = result.links[index]
- clipboardHelper(txt(link.name), link.url)
- }
- }
-
ACTION_CHROME_CAST_EPISODE -> {
startChromecast(activity, click.data)
}
diff --git a/app/src/main/res/values-ajp/strings.xml b/app/src/main/res/values-ajp/strings.xml
index 7a70ccc6e..aa861bd47 100644
--- a/app/src/main/res/values-ajp/strings.xml
+++ b/app/src/main/res/values-ajp/strings.xml
@@ -221,9 +221,7 @@
بسبِب أعطال إزا نحط على مستوى عالي كتير على الأجهزة يللي م بتساع كتير، متل تلفزيون \"أندرويد\".
شي غير
أفي هيدا التجديد
- نسوخ الرابط
مَشي بال آپ
- مشي بمتصفح الويب
مفيد لتجاوز المنع من مزود خدمة الإنترنت
مسلسل
غير الحجم
@@ -390,10 +388,8 @@
م قدرنا ننزل الإصدار الجديد تبع الآپ
المؤلفين
إضافة
- كاست ڤيديو ع الوَب
معقول يكون موجود أصلًا
مشتركينلو
- متصفح الوَب
كل اللغات
دايمًا كتوب ب أحرف كاپيتال، A بدل a
مشغل الڤيديو المفضل
@@ -422,7 +418,6 @@
خلصت
محي السجل
تجَدَد (من قديم للجديد)
- \"ڤي أل سي ميديا پلاير\"
كاميرا
وَب
أبجديًا (من الياء للألف)
@@ -504,7 +499,6 @@
مزامنة
شوفو معلومات عن المشكلة
مدعوم
- \"أم پي ڤي\"
ظبّط وقت الترجمة
افتتاح مختلط
مقطع دعائي
@@ -627,7 +621,6 @@
رح ينزل ب %s
الحلقة ال %2$d من الجزء ال%1$d رح تنزل ب
كاست مراية
- إف كاست
نقي جهاز الكاست
ويكي \"كلود ستريم\"
أكونتات
@@ -669,6 +662,5 @@
\n%s
صورة زغيرة مع التقريب وال تبعيد
بت حط صورة زغير من الڤيديو إنت و عم بت قرب أو ترجع بال ڤيديو
- MPV YTDL
بعد مش معمول لود لولا ترجمة
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 33a1172ca..d4958b6d3 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -233,8 +233,6 @@
مرآة كروم كاست
تشغيل في التطبيق
%s تشغيل في
- تشغيل في الويب
- نسخ الرابط
التحميل التلقائي
تحميل بجودات مختلفة
إعادة تحميل الروابط
@@ -464,10 +462,6 @@
فتح(تشغيل)
%1$s %2$d%3$s
المكونات الإضافية المحدثة %d
- VLC
- MPV
- اسقاط فيديو الويب
- متصفح الإنترنت
تخطي %s
الافتتاح
النهاية
@@ -653,7 +647,6 @@
قادم خلال %s
سيتم إصدار الحلقة %1$d من الموسم %2$d في
مرآة البث
- بث ف
حدد جهاز البث
CloudStream ويكي
إعدادات الأمان
@@ -695,6 +688,5 @@
\n%s
معاينة شريط البحث
تمكين معاينة الصورة المصغرة على شريط البحث
- MPV YTDL
لم يتم تحميل أي ترجمات بعد
\ No newline at end of file
diff --git a/app/src/main/res/values-ars/strings.xml b/app/src/main/res/values-ars/strings.xml
index 873c55bf3..d1efdbbc5 100644
--- a/app/src/main/res/values-ars/strings.xml
+++ b/app/src/main/res/values-ars/strings.xml
@@ -311,11 +311,9 @@
اخرون
تخطي هذا التحديث
.قد يتسبب في تأخير التحديثات لبضعة أيام .jsDelivr باستخدام GitHubيتجاوز حظر
- انسخ الرابط
الدرامات الآسيوية
في قائمة الانتظار
افتح في التطبيق
- افتح في المتصفح
مفيد لتجاوز حجب مزودي خدمة الإنترنت
مسلسل
تقييم
diff --git a/app/src/main/res/values-as/strings.xml b/app/src/main/res/values-as/strings.xml
index 0ed41795c..b001ab768 100644
--- a/app/src/main/res/values-as/strings.xml
+++ b/app/src/main/res/values-as/strings.xml
@@ -89,7 +89,6 @@
সম্পূৰ্ণ
সৰ্বজনীন তালিকা
বন্ধ কৰক
- VLC
বেটাৰী অপ্টিমাইজেচন নিষ্ক্ৰিয় কৰক
সদস্যতা গ্ৰহণ কৰা
গুণসমূহ
@@ -302,8 +301,6 @@
কাষ্ট মিৰৰ
ডাব ছপা
প্লে %s ত
- ব্ৰাউজাৰত প্লে কৰক
- লিংক কপি কৰক
স্বয়ংক্ৰিয় ডাউনলোড
ডাউনলোড মিৰৰ
সাব ছপা
@@ -498,10 +495,6 @@
HLS প্লেলিস্ট
পছন্দৰ ভিডিঅ\' প্লেয়াৰ
আভ্যন্তৰীণ প্লেয়াৰ
- MPV
- ৱেব ভিডিঅ\' কাষ্ট
- Fcast
- ৱেব ব্ৰাউজাৰ
কাষ্ট ডিভাইচ চয়ন কৰক
মিশ্ৰিত সমাপ্তি
মিশ্ৰিত উদ্ঘাটনী
@@ -650,7 +643,6 @@
ডিভাইচ পিন ক\'ড পোৱা নাই, স্থানীয় প্ৰমাণীকৰণ চেষ্টা কৰক
পিন ক\'ডৰ মেয়াদ শেষ হৈছে!
ক\'ড মেয়াদ শেষ হব %1$dm %2$ds
- MPV YTDL
সীকবৰ প্ৰিভিউ
সীকবৰত প্ৰিভিউ থাম্বনেইল সক্ৰিয় কৰক
আৰম্ভৰ পৰা প্লে কৰক
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 3579c8353..218fa5e57 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -238,8 +238,6 @@
Chromecast огледало
Пусни в приложението
Пусни в %s
- Пусни в браузър
- Копирай връзка
Автоматично изтегляне
Изтегляне на огледало
Презареждане на връзки
@@ -443,10 +441,6 @@
HLS плейлист
Предпочитан видео плеър
Вътрешен плеър
- VLC
- MPV
- Уеб видео предаване
- Уеб браузър
Приложението не е намерено
Автоматично изтегли добавки
Всички езици
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index c5ed86514..f1bc8f82a 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -230,7 +230,6 @@
প্লাগইন ডাউনলোড ফিল্টার করতে মোড নির্বাচন করুন
লিঙ্ক পুনরায় লোড হয়েছে
সুইচ অ্যাকাউন্ট
- ব্রাউজারে প্লে করুন
দাবিত্যাগ
এশিয়ান ড্রামা
সোর্স
@@ -274,7 +273,6 @@
টরেন্ট
এপিসোড ক্রোমকাস্ট করুন
প্লে হচ্ছে %s সময়ের মধ্যে
- লিঙ্ক কপি করুন
স্বয়ংক্রিয় ডাউনলোড
টাইটেল
প্লেয়ার দেখা যাচ্ছে - সিকের পরিমাণ
diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml
index 0ad12bdf4..a39c4e208 100644
--- a/app/src/main/res/values-bp/strings.xml
+++ b/app/src/main/res/values-bp/strings.xml
@@ -235,8 +235,6 @@
Alternativa pelo Chromecast
Assistir no App
Assistir no %s
- Assistir no navegador
- Copiar link
Auto download
Baixar por servidor alternativo
Recarregar links
@@ -538,16 +536,13 @@
Começar
Suportado
Status
- MPV
Abrindo mistura
- VLC
Reinicie o aplicativo para ver as alterações.
Visualização info de crash
Faixas de áudio
Adicionado em (novo para antigo)
Faixas de video
Legendas
- Navegador
18+
Links
Funcionalidades do Player
@@ -563,7 +558,6 @@
Vídeo
Android TV
Wi-Fi
- Lista de videos da web
A interface de usuário não foi gerada corretamente. Isto se trata de um bug importante e deve ser reportado imediatamente %s
Características da interface de usuário
Provedor de teste
@@ -642,7 +636,6 @@
Redefinir
Próximos em %s
Temporada %1$d Episódio %2$d será lançado em
- Fcast
Selecione o dispositivo de transmissão
Espelhar transmissão
CloudStream Wiki
@@ -685,6 +678,5 @@
Desmarcar todos
Ativar visualização de miniatura na barra de busca
Visualização da barra de busca
- MPV + YTDL
Ainda não há legendas carregadas
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 414f26440..6e6d3b2a1 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -223,8 +223,6 @@
Chromecast jako zrcadlo
Přehrát v aplikace
Přehrát ve %s
- Přehrát v prohlížeči
- Zkopírovat odkaz
Automaticky stáhnout
Zrcadlo stahování
Obnovit odkazy
@@ -400,8 +398,6 @@
Veřejný seznam
Velká písmena u všech titulků
Playlist HLS
- MPV
- Webové vysílání videa
Aplikace nenalezena
Přeskočit %s
Úvod
@@ -451,13 +447,11 @@
Popis
Stav
Nejprve nainstalujte rozšíření
- VLC
Smíšený konec
Jazyk
Interní přehrávač
Rekapitulace
Vymazat historii
- Webový prohlížeč
Všechny jazyky
Smíšený úvod
Poděkování
@@ -645,7 +639,6 @@
Vychází %s
Epizoda %2$d ze série %1$d bude vydána za
Vysílat zrcadlení
- Fcast
Vyberte zařízení k vysílání
CloudStream Wiki
Zabezpečení
@@ -687,6 +680,5 @@
Odstranit (%1$d | %2$s)
Náhled v liště přehrávače
Povolit náhled miniatur na liště přehrávače
- MPV YTDL
Zatím nenačteny žádné titulky
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index c9656d9de..6cf8ff3bd 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -241,8 +241,6 @@
Downloadfehler, bitte überprüfen sie die Speicherberechtigungen
Chromecast-Episode
In %s wiedergeben
- In Browser wiedergeben
- Link kopieren
Auto-Download
Alternativer Download
Links neu laden
@@ -437,10 +435,6 @@
HLS-Playlist
Bevorzugter Videoplayer
Interner Player
- VLC
- MPV
- Web Video Cast
- Browser
App nicht gefunden
Alle Sprachen
Überspringen %s
@@ -618,7 +612,6 @@
hide_player_control_names_key
Staffel %1$d Episode %2$d wird veröffentlicht in
Wird veröffentlicht in %s
- Fcast
Sicherheit
Konten
Repository öffnen
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index e22182d23..268ea7bd1 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -194,8 +194,6 @@
Chromecast επεισόδιο
Αναπαραγωγή εντός της εφαρμογής
Αναπαραγωγή σε %s
- Αναπαραγωγή στον περιηγητή
- Αντιγραφή συνδέσμου
Αυτόματη λήψη
Λήψη mirror
Επαναφόρτωση συνδέσμων
@@ -359,10 +357,6 @@
HLS Playlist
Προτεινόμενο πρόγραμμα αναπαραγωγής
Ενσωματωμένο πρόγραμμα αναπαραγωγής
- VLC
- MPV
- Web Video Cast
- Περιηγητής
Η εφαρμογή δεν βρέθηκε
%1$s Επ %2$d
Το επεισόδιο %d θα κυκλοφορήσει σε
@@ -580,7 +574,6 @@
Δευτερόλεπτα Σκιπ όταν φαίνεται ο αναπαραγωγέας (πλειερ)
Δοκιμή όλων των παροχών
Αυτό το τεστ προορίζεται μόνο για τους προγραμματιστές και δε επαληθείει ούτε απορρίπτει την λειτουργία οποιουδήποτε παρόχου.
- Fcast
Επιλογή συσκευής για αναμετάδοση
Πρόβλημα στην πρόσβαση στο Clipboard, Παρακαλώ προσπαθήστε ξανά.
Πρόβλημα στην αντιγραφή , Παρακαλούμε αντιγράψτε το logcat και επικοινωνήστε με την υποστήριξη.
diff --git a/app/src/main/res/values-es/array.xml b/app/src/main/res/values-es/array.xml
index eb197f43e..fddd832a5 100644
--- a/app/src/main/res/values-es/array.xml
+++ b/app/src/main/res/values-es/array.xml
@@ -152,32 +152,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index ccca67b36..2ec81cf28 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -57,11 +57,7 @@
Cantidad de búsquedas del reproductor (segundos)
Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura
Resolución del reproductor de video
- MPV
Reproductor
- VLC
- Web Video Cast
- Navegador Web
Iniciar el siguiente episodio cuando el actual termine
Omitir Intro
Apertura
@@ -78,9 +74,7 @@
Actualizar progreso de lo visto
Duplicar en Chromecast
No se encontraron Episodios
- Reproducir en Navegador
Reproducir en %s
- Copiar enlace
Descarga automática
Descargar desde servidor alternativo
Recargar enlaces
@@ -621,7 +615,6 @@
Próximamente en %s
La temporada %1$d y el episodio %2$d se estrenarán en
Seleccionar el dispositivo para transmitir
- Fcast
Espejo de transmisión
Wiki de CloudStream
Seguridad
@@ -663,6 +656,5 @@
\n%s
Activar la previsualización para las miniaturas en la barra de búsqueda
Previsualización de Seekbar
- MPV YTDL
Aún no hay subtítulos cargados
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 09746f1f8..0d40ce6ae 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -111,8 +111,6 @@
Miroir Chromecast
Lecture dans l\'application
Lecture dans %s
- Lecture dans le navigateur
- Copier le lien
Téléchargement automatique
Télécharger depuis le miroir
Recharger les liens
@@ -140,7 +138,7 @@
DNS avec HTTPS
Afficher les animés en Anglais (Dub) / sous-titrés
Disposition en mode téléphone
- %1$s Episode %2$d
+ episode_action_copy_link
Note : %.1f
Zoom
Adapter à l\'écran
@@ -430,12 +428,10 @@
Installer l\'extension d\'abord
Playlist HLS
Lecteur vidéo préféré
- VLC
Fin mitigée
Introduction mitigée
Installation de la mise a jour de l\'application…
Impossible d\'installer la nouvelle version de l\'application
- Navigateur Web
Certains téléphones ne supporte pas le nouvel installateur d\'application. Essayez l\'option de l\'ancien installateur si les mises-à-jour ne s\'installe pas.
Précédent
Ignorer la configuration
@@ -456,7 +452,6 @@
Lecteur interne
Application introuvable
Trop de texte. Impossible de sauvegarder dans le presse papier.
- MPV
Installateur de paquet
plugins
Cela supprimera également tous les plugins du repository
@@ -466,7 +461,6 @@
Langage
Afficher les popups skip pour les intro / fins
Ancienne méthode d\'installation
- Web Video Cast
Liens
Gestes
Fonctionnalités du lecteur
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index eed5e44aa..4457b0ec4 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -114,8 +114,6 @@
क्रोमकास्ट मिरर
एप्प में चलाएं
%s में चलाएं
- Browser में चलाएं
- लिंक कॉपी करें
डाउनलोड करें
मिरर डाउनलोड
लिंक दोबारा लोड करें
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 44f1bd5b9..dcacbeee3 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -253,8 +253,6 @@
Chromecast mirror
Pokreni u aplikaciji
Pokreni u %s
- Pokreni u pregledniku
- Kopiraj poveznicu
Automatsko preuzimanje
Preuzmi zrcalo
Ponovo učitaj poveznice
@@ -461,9 +459,6 @@
Preferirani video player
Interni player
Najprije instaliraj proširenje
- VLC
- MPV
- Emitiranje na webu
Aplikacija nije pronađena
Svi jezici
Previše teksta. Nije moguće spremiti u međuspremnik.
@@ -495,7 +490,6 @@
Zadane postavke
Izgledi
Značajke
- Web preglednik
Preskoči %s
Kraj
Sažetak
@@ -646,7 +640,6 @@
Vaši CloudStream podaci su sada spremljeni u sigurnosnu kopiju. Iako je vjerojatnost mala, neki se uređaji mogu ponašati drugačije. Ako izgubite pristup aplikaciji, potpuno izbrišite podatke aplikacije i obnovite ih pomoću sigurnosne kopije. Ispričavamo se zbog mogućih neugodnosti.
Sezona %1$d epizoda %2$d izlazi
Cast mirror
- Fcast
Odaberi uređaj za emitiranje
CloudStream Wiki
Računi
@@ -686,6 +679,5 @@
Stvarno želite trajno izbrisati sve epizode u sljedećoj seriji?
\n
\n%s
- MPV YTDL
Još nije učitan nijedan titl
\ No newline at end of file
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 1426e8a38..f124ad062 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -182,7 +182,6 @@
Dokumentumfilm
Ázsiai dráma
Linkek újratöltése
- Link másolás
Letöltés mirror
Automatikus letöltés
Adatok eltárolva
@@ -227,7 +226,6 @@
Chromecast mirror
Lejátszás az alkalmazásban
Lejátszás %s
- Lejátszás böngészőben
Feliratok letöltése
Újracsatlakozás…
Húzd balra vagy jobbra a videólejátszóban az idő vezérléséhez
@@ -352,7 +350,6 @@
Mit szeretnél látni
Minden %s már letöltött
Először telepítse a bővítményt
- Webböngésző
Kinézet
Alkalmazás elrendezés
Szinkronizálás
@@ -368,7 +365,6 @@
Töltse le az összes bővítményt ebből a tárolóból?
Biztonságos mód bekapcsolva
Méret
- MPV
Alkalmazás nem található
PackageInstaller
Rendezés e szerint
@@ -403,7 +399,6 @@
Emelt
HD
HLS lejátszási lista
- VLC
Nem sikerült telepíteni az alkalmazás új verzióját
%s hitelesítve
Körvonal
@@ -528,7 +523,6 @@
Hivatkozó (opcionális)
Nem találhatóak pluginek a repóban
Repó nem található, ellenőrizze a címet vagy próbálja VPN-el
- Web Videó Cast
%s kihagyása
A kihagyási felugró ablakok mutatása nyitás/zárás esetén
Alapbeállítás
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 22203b398..9255c459e 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -221,8 +221,6 @@
Mirror Chromecast
Putar di aplikasi
Putar di %s
- Putar di browser
- Salin tautan
Download otomatis
Download mirror
Muat ulang tautan
@@ -442,8 +440,6 @@
Bahasa
Pemutar video utama
Pemutar Bawaan
- VLC
- MPV
Terunduh %1$d %2$s
Memulai mengunduh %1$d %2$s…
Semua fitur tambahkan dimatikan karena crash, untuk memudahkanmu mencari penyebab crash.
@@ -495,9 +491,7 @@
Tidak
Memasang pembaruan…
Tidak dapat memasang versi terbaru
- Web browser
Aplikasi tidak ditemukan
- Web Video Cast
Hapus Riwayat
Tampilkan popup untuk skip sesi pembuka/akhir
Mengunduh pembaruan…
@@ -643,7 +637,6 @@
Akan datang di %s
Cermin Cast
Pilih perangkat cast
- Fcast
CloudStream Wiki
Keamanan
Akun
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 3e7636ce5..5902cd69e 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -243,8 +243,6 @@
Mirror Chromecast
Riproduci in app
Riproduci in %s
- Riproduci nel browser
- Copia link
Download
Mirror download
Aggiorna link
@@ -448,10 +446,6 @@
Playlist HLS
Video player preferito
Player interno
- VLC
- MPV
- Cast Web Video
- Web browser
App non trovata
Tutte le lingue
Salta %s
@@ -642,7 +636,6 @@
L\'episodio %2$d della stagione %1$d uscirà tra
Mirror cast
Seleziona dispositivo per cast
- Fcast
Wiki di CloudStream
Conti
Sicurezza
@@ -683,6 +676,5 @@
\n%s
Anteprima barra di ricerca
Abilita miniatura di anteprima sulla barra di ricerca
- MPV YTDL
Nessun sottotitolo caricato
\ No newline at end of file
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 6316d7f79..6ebe405bc 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -168,7 +168,6 @@
דרמה אסייתית
כרומקאסט את הפרק
כרומקאסט את המראה
- נגן בדפדפן
תווית כתוביות
החלף רכיבי ממשק משתמש בפוסטר
דלג על הפתיח
@@ -326,7 +325,6 @@
שגיאת הורדה, בדוק הרשאות אחסון
נגן באפליקציה
נגן ב %s
- העתק קישור
הורדה אוטומטית
טען מחדש קישורים
תווית איכות
@@ -431,7 +429,6 @@
כל %s כבר הורד
מחברים
שפה
- MPV
קרדיטים
מיין
בחר ספרייה
@@ -445,8 +442,6 @@
הורד את כל התוספים ממאגר זה?
רצועות שמע
מסלולים
- Web Video Cast
- דפדפן אינטרנט
כל התוספים נכבו עקב התרסקות כדי לעזור לך למצוא את האחד הגורם לצרות.
מוריד החבילות
עודכן %d תוספים
@@ -479,7 +474,6 @@
פלייליסט HLS
נגן וידאו מועדף
נגן פנימי
- VLC
האפליקציה לא נמצאה
כל השפות
דלג %s
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index ed9850e8e..3ca79b3d5 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -144,9 +144,6 @@
完成
進行中
デフォルト
- ウェブブラウザ
- VLC
- MPV
言語
作成者
サイズ
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 6993ec1c9..d62ecb69b 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -186,8 +186,6 @@
Chromecast 미러링
앱에서 재생
%s에서 재생
- 브라우저에서 재생
- 링크 복사
자동 다운로드
다운로드 미러
링크 새로고침
@@ -445,7 +443,6 @@
소개
유형
먼저 확장 프로그램을 설치하세요
- 웹 브라우저
앱을 찾을 수 없음
모든 언어
건너뛰기 %s
@@ -482,11 +479,8 @@
\n파일이 제거될 때까지 시작 시 확장 프로그램을 로드하지 않습니다.
HLS 재생목록
내부 플레이어
- MPV
선호하는 동영상 플레이어
- VLC
라이브러리 선택
- 웹 동영상 캐스트
이 목록이 비어 있습니다. 다른 목록으로 전환해 보세요.
필러
라이브 스트리밍 재생
@@ -579,7 +573,6 @@
자동 회전
모바일 데이터
사용 불가능
- fcast
캐스트 장치 선택
복사하는 중 오류가 발생했습니다. 로그캣을 복사하고 문의하십시오.
구독 취소
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index fe205dab7..cc68d77eb 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -163,10 +163,8 @@
Įvertinta
Praleisti šį atnaujinimą
Veiksmai
- Kopijuoti nuorodą
Paleisti programoje
Sinchronizuoti
- Paleisti naršyklėje
Pašalinti puslapį
Perkrauti nuorodos
Išjungti
@@ -192,7 +190,6 @@
Mobilūs duomenys
šaunusPrisijungimoVardas
Autoriai
- Naršyklė
Visos kalbos
4K
Pradėta siųsti %1$d %2$s…
@@ -206,7 +203,6 @@
Kalbos kodas (lt)
Baigta
Išvalyti istoriją
- VLC
Redaguoti
Wi-Fi
Greitai būs…
@@ -227,7 +223,6 @@
UHD
Dydis
Palaikoma
- MPV
ManoŠaunusPuslapis
Anonsas
Istorija
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 8f13b93c0..9469aa8e8 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -241,8 +241,6 @@
Chromecast morror
Palaist aplikācijā
Atskaņot uekšā %s
- Atskaņot internetā
- Kopēt linku
Automātiski ielādēt
Ielādēt spoguli
Pārlādēt saites
@@ -437,8 +435,6 @@
HLS atskaņošanas saraksts
Vēlamais video atskaņotājs
Iekšējais atskaņotājs
- MPV
- Web video apraide
Aplikācijs nav atrasta
Visas valodas
Beigas
@@ -513,8 +509,6 @@
Lejupielādējiet to vietņu sarakstu, kuras vēlaties izmantot
Vispirms instalējiet paplašinājumu
Atvēršana
- VLC
- Interneta mekletājs
Sākums
Izlaist %s
Noņemt no skatītajiem
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 671257492..79dc1ee7e 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -164,8 +164,6 @@
Огледало на Chromecastr
Пушти во апликацијата
Пушти на %s
- Пушти на прелистувач
- Копирај линк
Авто превземање
Превземи Mirror
Вчитај повторно врски
@@ -297,7 +295,6 @@
Износот на барањето што се користи кога плеерот е скриен
Преземи преводи
Јавна листа
- MPV
Инсталатор на пакети
ОВА
Ажурирање и резервна копија
@@ -425,7 +422,6 @@
/??
hello@world.com
+30
- VLC
Рестартирај
Цртан филм
Почна да презема %1$d %2$s…
@@ -448,7 +444,6 @@
Камера
Камера
SDR
- Веб-прелистувач
Апликацијата не е пронајдена
Корисничко име
Отвори со
@@ -488,7 +483,6 @@
Сите екстензии беа исклучени поради пад за да ви помогнат да ја пронајдете онаа што предизвикува проблеми.
Оцена: %s
Големина
- Веб-видео Cast
Сите јазици
Исчисти историја
Обележи како гледано
@@ -611,7 +605,6 @@
Сега е направена резервна копија на вашите податоци на CloudStream. Иако можноста за ова е многу мала, сите уреди можат да се однесуваат поинаку. Во ретки случаи, кога ќе се заклучите од пристап до апликацијата, целосно исчистете ги податоците на апликацијата и вратете ги од резервна копија. Многу ни е жал за какви било непријатности што произлегуваат од ова.
Ресетирај
Сезона %1$d Епизода %2$d ќе биде објавена за
- Fcast
Одбери уред да кастираш
Оневозможи оптимизација на батерија
Отклучи CloudStream
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index 1c2d855e5..bf0fede72 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -151,8 +151,6 @@
Chromecast Mirror -->
ആപ്പിൽ പ്ലേയ് ചെയ്യുക
%sയിൽ പ്ലേയ് ചെയ്യുക
- ബ്രൗസറിൽ പ്ലേയ് ചെയ്യുക
- ലിങ്ക് പകർത്തുക
ഡൌൺലോഡ് ചെയ്യൂ
മിറർ ഡൗണ്ലോഡ്
ലിങ്ക്സ് വീണ്ടും ലോഡുചെയ്യുക
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 07154776d..e865d58db 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -2,7 +2,6 @@
Semua bahasa
Langkau %s
- Pelayar web
Sejarah
Kosongkan sejarah
Pengenalan
@@ -232,7 +231,6 @@
Dinaikkan
Lihat video dalam bahasa-bahasa ini
Normal
- Main dalam pelayar
Tambah
Diguna
Anime
@@ -323,7 +321,6 @@
Chromecast episod
Main dalam %s
Muat turun gagal, cek keizinan storan
- Salin pautan
Muat turun cermin
Muat turun sari kata
Label sub
@@ -449,8 +446,6 @@
Pengesahan Password/PIN
Sari kata belum tetapkan lagi
Disokong
- MPV
- Fcast
Ralat tidak dapat akses Clipboard, Sila cuba sekali lagi.
Ralat menyalin, Sila salin logcat dan hubungi penyokong aplikasi.
Amaran
@@ -462,8 +457,6 @@
Maks
Amaran: CloudStream 3 tidak bertanggungjawab atas penggunaan tambahan pihak ketiga dan tidak memberi sumbang kepada mereka!
Mula semula aplikasi untuk lihat perubahan.
- VLC
- MPV YTDL
Senarai ini kosong. Sila tukar yang lain.
Data mudah alih
Tambah ke kegemaran
diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml
index 31e6ef276..9d82dd47d 100644
--- a/app/src/main/res/values-my/strings.xml
+++ b/app/src/main/res/values-my/strings.xml
@@ -157,8 +157,6 @@
Chromecast ဖန်သားပြင်
အက်ပ်တွင်းဖွင့်
ဖွင့်ရန် %s
- ဘရောက်ဇာထဲမှာ ဖွင့်ရန်
- လင့်ကူးယူရန်
အလိုအလျောက်ဒေါင်းလုဒ်
လင့်များကို ပြန်စစ်ရန်
အရည်အသွေး အမှတ်အသား
@@ -338,7 +336,6 @@
နောက်သို့
အပ်ဒိတ်လုပ်ပြီး %d ဖြည့်စွက်များ
ဒေါင်းလုဒ်မလုပ်ရသေး: %d
- ဝဘ်ဘရောက်ဇာ
အက်ပ်မတွေ့ပါ
ဘာသာစကားအားလုံး
ကျော်ရန် %s
@@ -422,9 +419,6 @@
ထောက်ပံ့ထားသော
ဘာသာစကား
အဆက်များကိုအရင်သွင်းပါ
- VLC
- MPV
- ဝဘ်ထဲတွင်ဖွင့်ရန်
အစပိုင်း
အဆုံးပိုင်း
ကြည့်ရှုခဲ့သည်များကိုရှင်းရန်
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 4bef20cc2..6518eb0e7 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -235,8 +235,6 @@
Chromecast mirror
Speel in app
Speel in %s
- Speel in browser
- Kopieer link
Automatisch downloaden
Download mirror
Herlaad Linkss
@@ -409,7 +407,6 @@
Kan %s niet laden
Alle %s reeds gedownload
plugin
- VLC
Sla %s over
Links
App updates
@@ -468,10 +465,7 @@
Herhaal installatieproces
Automatisch bijwerken plugin
Sommige telefoons ondersteunen het nieuwe installatieprogramma niet. Probeer de oude optie als de updates niet worden geïnstalleerd.
- Web Video Cast
Interne speler
- MPV
- Web browser
Gemengd einde
Herhaling
Start
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
index 33ebe1b51..5b5577c2e 100644
--- a/app/src/main/res/values-nn/strings.xml
+++ b/app/src/main/res/values-nn/strings.xml
@@ -149,8 +149,6 @@
Kjeldefeil
Spel av i programmet
Spel av i %s
- Spel av i nettlesaren
- Kopier lenke
Automatisk nedlasting
Last inn lenker på nytt
Last ned undertekstar
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index 41117f104..ac13f57f9 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -171,8 +171,6 @@
Støpt Speil
Spill i appen
Spill i %s
- Spill i nettleseren
- Kopier link
Automatisk nedlasting
Last ned speil
Last inn lenker på nytt
@@ -250,7 +248,6 @@
HLS-spilleliste
Foretrukket videospiller
Intern spiller
- VLC
Alle språk
Hopp over %s
Tøm historikk
@@ -283,7 +280,6 @@
Beskrivelse
Legg til konto
Normal
- MPV
Dobbelttrykk for å sette på pause
+30
Skygge
@@ -416,7 +412,6 @@
Kunne ikke logge inn på %s
Store bokstaver i undertekster
Utviklere
- Nettleser
Sensurerbart
Vev
Lenke til strøm
@@ -450,7 +445,6 @@
Fjern unødvendig informasjon fra undertekster
Ekstra
Filtrer etter foretrukket mediaspråk
- Vev-videosending
Tilbakeblikk
SD
Forfilm
diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml
index a9cff7edf..1a7d9c72f 100644
--- a/app/src/main/res/values-or/strings.xml
+++ b/app/src/main/res/values-or/strings.xml
@@ -28,14 +28,12 @@
ଟି ଅଧ୍ୟାୟ
ଅଧ୍ୟାୟ
%sରେ ଚଲାଅ
- ବ୍ରାଉଜର୍ରେ ଚଲାଅ
ଉପଶୀର୍ଷକ ଡାଉନଲୋଡ୍ କରିବା
/%d
/??
ଅଧ୍ୟାୟ %d ମୁକ୍ତିଲାଭ କଲା!
ସ୍ୱତଃ ଡାଉନଲୋଡ୍
ଲିଙ୍କ୍ଗୁଡ଼ିକୁ ପୁନଃଲୋଡ୍ କରିବା
- ଲିଙ୍କ୍ କପି କରିନେବା
ଆପ୍ରେ ଚଲାଅ
Chromecast ଅଧ୍ୟାୟ
ଅ
@@ -61,8 +59,6 @@
ପ୍ରାନ୍ତ
ଆପ୍ ମିଳିଲା ନାହିଁ
ସବୁ ଭାଷା
- VLC
- MPV
ମିଶ୍ରିତ ପ୍ରାନ୍ତ
ମିଶ୍ରିତ ଆଦ୍ୟ
ଶ୍ରେୟ
diff --git a/app/src/main/res/values-pl/array.xml b/app/src/main/res/values-pl/array.xml
index a43d7bcfe..45a8e56e1 100644
--- a/app/src/main/res/values-pl/array.xml
+++ b/app/src/main/res/values-pl/array.xml
@@ -161,32 +161,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 0974257b0..f905c5f7b 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -232,8 +232,6 @@
Mirror dla Chromecast
Odtwórz w aplikacji
Odtwórz w %s
- Odtwórz w przeglądarce
- Kopiuj link
Automatyczne pobieranie
Pobierz mirror
Odśwież linki
@@ -421,10 +419,6 @@
Playlista HLS
Preferowany odtwarzacz wideo
Odtwarzacz wewnętrzny
- VLC
- MPV
- Web Video Cast
- Przeglądarka
Aplikacja nie została znaleziona
Wszystkie języki
Wyczyść historię
@@ -621,7 +615,6 @@
Resetuj
Nadchodzące w %s
Odcinek %2$d sezonu %1$d wyjdzie za
- Fcast
Wybierz urządzenie do transmisji
Mirror transmisji
Wiki CloudStream
@@ -664,6 +657,5 @@
Usuń (%1$d | %2$s)
Podgląd paska przewijania
Włącz podgląd miniatury na pasku wyszukiwania
- MPV YTDL
Nie wczytano jeszcze napisów
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 421746742..fd3fa52d9 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -228,8 +228,6 @@
Alternativa pelo Chromecast
Reproduzir na app
Reproduzir no %s
- Reproduzir no navegador
- Copiar link
Transferência Automática
Transferir por servidor alternativo
Recarregar links
@@ -441,18 +439,14 @@
Abertura
Selecionar Biblioteca
Contorna o bloqueio de URLs raw do GitHub usando jsDelivr. Pode atrasar as atualizações por uns dias.
- VLC
Todas as linguagens
Atualizado (Novo para Antigo)
Inscrito
HDR
Reiniciar
- Navegador Web
Atualizado (Antigo para Novo)
- Web Video Cast
DVD
Instalador de pacotes
- MPV
Remover dos assistidos
Não foi possível instalar a nova versão do aplicativo
Inscrição cancelada em %s
@@ -617,7 +611,6 @@
Para garantir descarregamentos ininterruptos e notificações de programas de TV subscritos, o CloudStream precisa de permissão para ser executado em segundo plano. Ao premir OK, será direcionado para informações da aplicação. Aí, desloque-se para utilização da bateria da aplicação e defina a utilização da bateria para sem restrições. Tenha em atenção que esta permissão não significa que o CS3 irá esgotar a sua bateria. Este só funcionará em segundo plano quando necessário, como ao receber notificações ou baixar vídeos de extensões oficiais. Se optar por cancelar, pode ajustar esta definição mais tarde em definições gerais.
Reiniciar
Episódio %1$d Episódio %2$d vai ser lançado em
- Fcast
Escolha o dispositivo
Transmitir
hide_player_control_names_key
diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml
index 378e3aaec..8f0e14cbc 100644
--- a/app/src/main/res/values-qt/strings.xml
+++ b/app/src/main/res/values-qt/strings.xml
@@ -144,8 +144,6 @@
aoohaaahhu ahouuhhh
ooo-ahahaauuh aaahhu
ooo-ahah ohaauuh %s
- ahoha ooo-ahahohoohah oooohh
- aauugghhahhaauugghh
aaaghhoooohh aaahhu ahooo
ohooo-ahahaohaohahhhoouuh
ahoooaaahhuahaaahhuoha
@@ -318,7 +316,6 @@
aaahh uuuugggh
oooohh oooogggoog
uuuuhhhaagg
- uuh
uuh aahh uugg oooogg ag aagg ug ooooggguh
ooooggg %d
aahh
@@ -472,8 +469,6 @@
ooh oooohhhooh oogg oooogg ooh uuh uh g ooogg oh uuhh uug uuhh ooh aah uuuuggg ooooggg
ooooggg
aaaagggh uuuugg
- ooh uuuhh aahh
- uuh uuuuhhh
aah ooh uuugg
uuuuhhh
uuuuhh aagg oooohhh
@@ -617,8 +612,6 @@
%s (Disabled)
Rating: %s
aaaagg
- oog
- uuuhh
aaaagg aahh oooohh
uuuuuk
aaagg aaaahhh
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 8105aa3e8..ec1115112 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -228,8 +228,6 @@
Chromecast alternativ
Redă în Aplicație
Redă în %s
- Redă în Browser
- Copiază link-ul
Auto-descărcare
Descărcă prin Alernativă
Reîncărcare link-uri
@@ -467,7 +465,6 @@
Nu
Abonat la %s
Aplicația va fi actualizată la ieșire
- Web Video Cast
Ocoliri ISP
Anterior
Sortează
@@ -475,7 +472,6 @@
Filtrați în funcție de limba media preferată
Episodul %d a fost lansat!
Android TV
- VLC
Urmăriți videoclipuri în aceste limbi
Revenire
Acțiuni
@@ -483,7 +479,6 @@
URL invalid
Toate extensiile au fost dezactivate din cauza unei defecțiuni pentru a vă ajuta să o găsiți pe cea care cauzează probleme.
Se descarcă actualizarea aplicației…
- Browser web
CloudStream nu are niciun site instalat din start. Trebuie să instalați site-urile din depozite.
\n
\nAlăturați-vă Discord-ului nostru sau căutați online.
@@ -522,7 +517,6 @@
Se actualizează emisiunile abonate
Abonat
Lista publică
- MPV
Moştenit
Test de furnizor
Furnizori
@@ -640,6 +634,5 @@
Sezonul %1$d Episod %2$d va fi lansat în
Selectați divece-ul pe care doriți să faceți cast
Cast mirror
- Fcast
hide_player_control_names_key
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 809f12ce4..2b0402566 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -190,7 +190,6 @@
Торренты
Другое
Ошибка загрузки, проверьте разрешения хранилища
- Копировать ссылку
Автоскачивание
Загрузка. Зеркало
Сезон
@@ -270,8 +269,6 @@
Размер
Авторы
Поддерживается
- VLC
- MPV
Пропустить %s
Концовка
Используйте яркость системы в проигрывателе приложения вместо темного наложения
@@ -293,7 +290,6 @@
Неожиданная ошибка плеера
Эпизод Chromecast
Воспроизведение на %s
- Воспроизвести в браузере
Скачать субтитры
Знак качества
Переключение элементов интерфейса на плакате
@@ -309,7 +305,6 @@
%d из 10
Посмотреть информацию о сбое
Предпочитаемый видеоплеер
- Веб-браузер
Приложение не найдено
Все языки
Вступление
@@ -492,7 +487,6 @@
Отображать рандомную кнопку в библиотеке и главной странице
Рандомная кнопка
Legacy (старый)
- Web Video Cast
Не отправляет данные
Перезагрузить ссылки
Предпочтительные медиа
@@ -620,7 +614,6 @@
Сброс
Сезон %1$d Эпизод %2$d выйдет
Выйдет %s
- Fcast
Выберите девайс для трансляции
hide_player_control_names_key
В данный момент загрузок нет.
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 4e38be6b9..196ed1d60 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -188,7 +188,6 @@
Žiadne titulky
Anime
Kreslené
- Skopírovať odkaz
Automaticky stiahnuť
Zrkadlo sťahovania
Zamknúť
@@ -320,7 +319,6 @@
Ázijské drámy
Anime
Chyba zdroja
- Prehrať v prehliadači
Štítok dabingu
Štítok titulkov
Názov
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index e4ee98f96..8b1e4cc15 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -119,7 +119,6 @@
Asalkiisa
Isla appkan ku daawo
Ku daawo %s
- Ku daawo barawsarka
Dejinta iskeed ah
Deji toorentiga(mirror)
Cusbooneysii lifaaqyada
@@ -239,7 +238,6 @@
Eeg xogaha hoose 🐈
Badhinka xajmiga daaraha
Fashil ka yimi dhiibaha
- Koobu garee lifaaqa
Sharraxaad ma leh
Sharraxaadda
Duluc ma leh
@@ -472,10 +470,6 @@
Kordhiyeyaasha
Liiska bulshada kale
Muqaal daaraha appka
- VLC
- MPV
- Web Video Cast
- Barawsarka
Kuuguma jiro appkaasi
Dhammaan luuqadaha
Is dhaafi %s
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index cb695c0f9..601500ff7 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -148,8 +148,6 @@
Chromecasta en Länk
Spela upp i appen
Spela upp i %s
- Spela upp i webbläsaren
- Kopiera länk
Automatisk nerladdning
Ladda ner en specifik länk
Ladda om alla länkar
@@ -379,13 +377,11 @@
Visa sub
Kunde inte öppna appen
GitHub Proxy
- Webbläsarens videospelare
Installerar uppdatering till appen…
Kunde inte nå GitHub, sätter på jsDelivr proxy…
Leverantörer
Nytt webbplatsnamn
Ta bort reklam från undertexter
- VLC
Alla språk
Rensa historik
PackageInstaller
@@ -409,8 +405,6 @@
Videospelare
Öppna med
Synkronisera undertexter
- MPV
- Web Video Cast
Misslyckades
Ja
Videospår
@@ -619,7 +613,6 @@
Kommer ut om %s
Fel vid kopiering, kopiera logcat och kontakta appsupport.
Media
- Fcast
Cast mirror
Säsong %1$d Avsnitt %2$d kommer att släppas om
Välj cast-enhet
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index c8c3243cd..a2b4cadd2 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -266,8 +266,6 @@
மறுதொடக்கம்
விவரம்
ஆசிரியர்கள்
- வலை வீடியோ நடிகர்கள்
- இணைய உலாவி
%s ஐத் தவிர்க்கவும்
மறுபரிசீலனை செய்யுங்கள்
அறிமுகம்
@@ -365,7 +363,6 @@
முன்மாதிரி தளவமைப்பு
பதிவிறக்கம் செய்யப்பட்ட கோப்பு
பகுத்தல்
- எம்.பி.வி.
உங்கள் நூலகம் காலியாக உள்ளது :(
\n நூலகக் கணக்கில் உள்நுழைக அல்லது உங்கள் உள்ளக நூலகத்தில் காட்சிகளைச் சேர்க்கவும்.
குழுவிலகவும்
@@ -431,8 +428,6 @@
மூல பிழை
தொலை பிழை
ரெண்டரர் பிழை
- உலாவியில் விளையாடுங்கள்
- இணைப்பை நகலெடுக்கவும்
ஆட்டோ பதிவிறக்கம்
கண்ணாடியைப் பதிவிறக்கவும்
இணைப்புகளை மீண்டும் ஏற்றவும்
@@ -520,7 +515,6 @@
அளவு
ஆதரிக்கப்பட்டது
முதலில் நீட்டிப்பை நிறுவவும்
- Fcast
காச்ட் சாதனத்தைத் தேர்ந்தெடுக்கவும்
அனைத்து மொழிகளும்
ஆம்
@@ -551,7 +545,6 @@
பதிவிறக்கம் செய்யப்படவில்லை: %d
புதுப்பிக்கப்பட்டது %d செருகுநிரல்கள்
உள் வீரர்
- வி.எல்.சி.
திறப்பு
கலப்பு திறப்பு
வரவு
diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml
index d832144dd..5fd25031e 100644
--- a/app/src/main/res/values-tl/strings.xml
+++ b/app/src/main/res/values-tl/strings.xml
@@ -174,8 +174,6 @@
Chromecast Mirror
I-play sa App
I-play sa %s
- I-play sa browser
- Kopyahin ang Link
Awtomatiking i-download
Download mirror
Subukan muli
diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml
index 22a94ebf0..4ec45e6d1 100644
--- a/app/src/main/res/values-tr/array.xml
+++ b/app/src/main/res/values-tr/array.xml
@@ -33,22 +33,6 @@
- 6
-
- - @string/player_settings_play_in_app
- - @string/player_settings_play_in_vlc
- - @string/player_settings_play_in_mpv
- - @string/player_settings_play_in_web
- - @string/player_settings_play_in_browser
-
-
-
- - 1
- - 2
- - 5
- - 4
- - 3
-
-
- @string/resolution_and_title
- @string/title
@@ -187,32 +171,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 3e3c0e208..46123d7ab 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -258,8 +258,6 @@
Bağlantıyı Chromecast ile yayınla
Burada oynat
%s üzerinden oynat
- Tarayıcıda oynat
- Bağlantıyı kopyala
Otomatik indir
Şu kaynaktan indir
Bağlantıları yenile
@@ -482,10 +480,6 @@
HLS Oynatma Listesi
Tercih edilen video oynatıcısı
Dahili oynatıcı
- VLC
- MPV
- Web Video Yayını
- İnternet tarayıcısı
Uygulama bulunamadı
Geçmiş
İzlendi olarak işaretle
@@ -669,7 +663,6 @@
Sezon %1$d Bölüm %2$d tarihinde yayınlanacak
Yansıtılacak cihaz seç
Ekran yansıtma
- Fcast
CloudStream Viki
Güvenlik
Hesaplar
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 479bdc38b..7cf754f97 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -248,7 +248,6 @@
Змінити розмір
Короткий зміст
Фільми
- Скопіювати посилання
Перезавантажити посилання
Документальні фільми
NSFW
@@ -257,7 +256,6 @@
Торент
Мітка якості
NSFW
- Переглянути в браузері
Несподівана помилка плеєра
Помилка завантаження, перевірте дозволи на зберігання
Епізод Chromecast
@@ -445,10 +443,6 @@
Спочатку встановіть розширення
Список відтворення HLS
Вбудований плеєр
- VLC
- MPV
- Web Video Cast
- Веббраузер
Ендінґ
Коротке повторення
Пропустити %s
@@ -620,7 +614,6 @@
Скинути
Наступний через %s
%1$d сезон %2$d епізод вийде через
- Fcast
Оберіть пристрій для трансляції
Трансляція через дзеркало
CloudStream Wiki
@@ -663,6 +656,5 @@
\n%s
Попередній перегляд повзунка
Ввімкнути мініатюру попереднього перегляду на повзунку
- MPV YTDL
Субтитри ще не завантажено
\ No newline at end of file
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 5a32dfe8a..06508bc9e 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -243,7 +243,6 @@
Chromecast mirror
ایپ میں چلائیں
%s میں چلائیں
- کاپی لنک
ڈاؤنلوڈ mirror
لنکس کو دوبارہ لوڈ کریں
سب ٹائٹلز ڈاؤن لوڈ
@@ -336,7 +335,6 @@
کوئی زیرنویس میں دیری نہیں
این ایس ایف ڈبلیو
آٹو ڈاؤن لوڈ
- browser میں چلائیں
بہت زیادہ سیٹ ہونے پر کم میموری والی ڈیوائس(جیسے کہ Android TV) پر کریشوں کا سبب بنتا ہے.
Remove site
Add a clone of an existing site, with a different URL
@@ -361,7 +359,6 @@
قرارداد اور عنوان
HDR
%s سے ان سبسکرائب کیا گیا
- ویب ویڈیو کاسٹ
18+
لاگ
https://example.com/example.mp4
@@ -427,11 +424,8 @@
حالت
سائز
زبان
- وی ایل سی
ترجیحی ویڈیو پلیئر
اندرونی پلیئر
- ایم پی وی
- ویب براؤزر
ایپ نہیں ملی
Recap
مخلوط اختتام
diff --git a/app/src/main/res/values-vi/array.xml b/app/src/main/res/values-vi/array.xml
index f363befda..c6ff1c4e2 100644
--- a/app/src/main/res/values-vi/array.xml
+++ b/app/src/main/res/values-vi/array.xml
@@ -153,32 +153,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 53b19bd8b..5c4cd380e 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -243,8 +243,6 @@
Chiếu Chromecast
Xem với trình phát mặc định
Xem với trình phát %s
- Xem tại trình duyệt
- Sao chép liên kết
Tự động tải xuống
Nguồn tải xuống
Lấy link mới nhất
@@ -472,10 +470,6 @@
Hỗ trợ
Ngôn ngữ
Cài đặt tiện ích trước
- VLC
- MPV
- Web Video Cast
- Trình duyệt web
Không thấy ứng dụng
Tất cả ngôn ngữ
Tua %s
@@ -671,11 +665,9 @@
\n
\n%s
Xóa plugin
- Fcast
Ngày phát hành (Cũ đến mới)
Ẩn tên các nút điều khiển
Bật chế độ xem trước hình thu nhỏ trên seekbar
Xem trước Seekbar
- MPV YTDL
Chưa tải phụ đề
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index b8b8b4884..3c002c29a 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -258,8 +258,6 @@
Chromecast 鏡像
在應用程式中播放
在 %s 中播放
- 在瀏覽器中播放
- 複製連結
自動下載
下載鏡像
重新載入連結
@@ -482,10 +480,6 @@
HLS 播放清單
偏好影片播放器
內部播放器
- VLC
- MPV
- 網路影片播放
- 網頁瀏覽器
未找到應用程式
所有語言
跳過 %s
@@ -663,7 +657,6 @@
使用指紋、面容 ID、PIN、圖案和密碼解除鎖定應用程式。
由於多次嘗試失敗,此畫面已關閉。請重新啟動應用程式。
剪貼簿存取失敗,請再試一次。
- Fcast
複製失敗,請複製 logcat 內容並聯繫應用程式支援者。
無法開啟 CloudStream 的應用程式資訊頁面。
您的 CloudStream 資料已完成備份。儘管可能性非常低,但因不同裝置的行為都有所不同,在極少數情況下,您可能會無法存取本應用程式。此時請完全清除本應用程式的資料,再使用已有的備份進行還原。若因此造成任何不便,我們深感抱歉。
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index ff85df32e..45e350439 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -259,8 +259,6 @@
Chromecast 镜像
在应用中播放
在 %s 中播放
- 在浏览器中播放
- 复制链接
自动下载
下载镜像
重新加载链接
@@ -483,10 +481,6 @@
HLS 播放列表
首选视频播放器
内部播放器
- VLC
- MPV
- 投屏
- 浏览器
未找到应用
所有语言
跳过 %s
@@ -665,7 +659,6 @@
在多次尝试失败后,提示将关闭。只需重启应用程序再试。
即将在 %s
CloudStream Wiki
- Fcast
选择投射设备
%1$d季%2$d集将在
投射镜像
@@ -674,7 +667,6 @@
打开本地视频
安全
访问智能手机或电脑上的 %s 并输入上述代码
- MPV YTDL
进度条预览
启用进度条预览缩略图
删除插件
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index b077997fe..a987420e9 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -50,24 +50,6 @@
- @string/nsfw
-
- - @string/player_settings_play_in_app
- - @string/player_settings_play_in_vlc
- - @string/player_settings_play_in_mpv
- - @string/player_settings_play_in_mpvytdl
- - @string/player_settings_play_in_web
- - @string/player_settings_play_in_browser
-
-
-
- - 1
- - 2
- - 5
- - 6
- - 4
- - 3
-
-
- @string/resolution_and_title
- @string/title
@@ -226,32 +208,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7c7553208..c35c2b9d0 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -381,8 +381,6 @@
Cast mirror
Play in app
Play in %s
- Play in browser
- Copy link
Auto download
Download mirror
Reload links
@@ -662,12 +660,6 @@
HLS Playlist
Preferred video player
Internal player
- VLC
- MPV
- MPV YTDL
- Web Video Cast
- Fcast
- Web browser
Select cast device
App not found
All Languages
From f7594e523992da3b0805ae1c1e82664e20b552d2 Mon Sep 17 00:00:00 2001
From: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Date: Sun, 22 Sep 2024 20:56:34 +0200
Subject: [PATCH 005/956] VideoClickAction api (#1329)
---
app/src/main/AndroidManifest.xml | 9 +-
.../lagradost/cloudstream3/CommonActivity.kt | 28 +-
.../lagradost/cloudstream3/MainActivity.kt | 115 +----
.../cloudstream3/actions/OpenInAppAction.kt | 134 ++++++
.../cloudstream3/actions/VideoClickAction.kt | 87 ++++
.../actions/temp/CopyClipboardAction.kt | 27 ++
.../cloudstream3/actions/temp/MpvKtPackage.kt | 69 +++
.../cloudstream3/actions/temp/MpvPackage.kt | 61 +++
.../actions/temp/PlayInBrowserAction.kt | 44 ++
.../actions/temp/ViewM3U8Action.kt | 30 ++
.../cloudstream3/actions/temp/VlcPackage.kt | 68 +++
.../actions/temp/WebVideoCastPackage.kt | 62 +++
.../actions/temp/fcast/FcastAction.kt | 67 +++
.../temp}/fcast/FcastManager.kt | 2 +-
.../temp}/fcast/FcastSession.kt | 2 +-
.../{utils => actions/temp}/fcast/Packets.kt | 2 +-
.../lagradost/cloudstream3/plugins/Plugin.kt | 14 +
.../cloudstream3/plugins/PluginManager.kt | 7 +
.../cloudstream3/ui/ControllerActivity.kt | 19 +-
.../ui/player/DownloadFileGenerator.kt | 6 +-
.../ui/player/ExtractorLinkGenerator.kt | 9 +-
.../cloudstream3/ui/player/IGenerator.kt | 59 +--
.../cloudstream3/ui/player/LinkGenerator.kt | 6 +-
.../ui/player/PlayerGeneratorViewModel.kt | 7 +-
.../ui/player/RepoLinkGenerator.kt | 10 +-
.../cloudstream3/ui/result/EpisodeAdapter.kt | 29 +-
.../ui/result/ResultViewModel2.kt | 436 +++---------------
.../ui/settings/SettingsPlayer.kt | 18 +-
app/src/main/res/values-ajp/strings.xml | 8 -
app/src/main/res/values-ar/strings.xml | 8 -
app/src/main/res/values-ars/strings.xml | 2 -
app/src/main/res/values-as/strings.xml | 8 -
app/src/main/res/values-bg/strings.xml | 6 -
app/src/main/res/values-bn/strings.xml | 2 -
app/src/main/res/values-bp/strings.xml | 8 -
app/src/main/res/values-cs/strings.xml | 8 -
app/src/main/res/values-de/strings.xml | 7 -
app/src/main/res/values-el/strings.xml | 7 -
app/src/main/res/values-es/array.xml | 26 --
app/src/main/res/values-es/strings.xml | 8 -
app/src/main/res/values-fr/strings.xml | 8 +-
app/src/main/res/values-hi/strings.xml | 2 -
app/src/main/res/values-hr/strings.xml | 8 -
app/src/main/res/values-hu/strings.xml | 6 -
app/src/main/res/values-in/strings.xml | 7 -
app/src/main/res/values-it/strings.xml | 8 -
app/src/main/res/values-iw/strings.xml | 6 -
app/src/main/res/values-ja/strings.xml | 3 -
app/src/main/res/values-ko/strings.xml | 7 -
app/src/main/res/values-lt/strings.xml | 5 -
app/src/main/res/values-lv/strings.xml | 6 -
app/src/main/res/values-mk/strings.xml | 7 -
app/src/main/res/values-ml/strings.xml | 2 -
app/src/main/res/values-ms/strings.xml | 7 -
app/src/main/res/values-my/strings.xml | 6 -
app/src/main/res/values-nl/strings.xml | 6 -
app/src/main/res/values-nn/strings.xml | 2 -
app/src/main/res/values-no/strings.xml | 6 -
app/src/main/res/values-or/strings.xml | 4 -
app/src/main/res/values-pl/array.xml | 26 --
app/src/main/res/values-pl/strings.xml | 8 -
app/src/main/res/values-pt/strings.xml | 9 +-
app/src/main/res/values-qt/strings.xml | 7 -
app/src/main/res/values-ro/strings.xml | 7 -
app/src/main/res/values-ru/strings.xml | 7 -
app/src/main/res/values-sk/strings.xml | 2 -
app/src/main/res/values-so/strings.xml | 6 -
app/src/main/res/values-sv/strings.xml | 7 -
app/src/main/res/values-ta/strings.xml | 7 -
app/src/main/res/values-tl/strings.xml | 2 -
app/src/main/res/values-tr/array.xml | 42 --
app/src/main/res/values-tr/strings.xml | 7 -
app/src/main/res/values-uk/strings.xml | 8 -
app/src/main/res/values-ur/strings.xml | 6 -
app/src/main/res/values-vi/array.xml | 26 --
app/src/main/res/values-vi/strings.xml | 8 -
app/src/main/res/values-zh-rTW/strings.xml | 7 -
app/src/main/res/values-zh/strings.xml | 8 -
app/src/main/res/values/array.xml | 44 --
app/src/main/res/values/strings.xml | 10 +-
app/src/main/res/xml/settings_player.xml | 2 +-
81 files changed, 839 insertions(+), 1048 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/FcastManager.kt (98%)
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/FcastSession.kt (96%)
rename app/src/main/java/com/lagradost/cloudstream3/{utils => actions/temp}/fcast/Packets.kt (95%)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1aeef5550..a04504acd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,7 +17,7 @@
-
+
@@ -30,13 +30,6 @@
android:name="android.software.leanback"
android:required="false" />
-
-
-
-
-
-
-
- val resultCode = result.resultCode
- val data = result.data
- if (resultCode == AppCompatActivity.RESULT_OK && data != null && resumeApp.position != null && resumeApp.duration != null) {
- val pos = resumeApp.getPosition(data)
- val dur = resumeApp.getDuration(data)
- if (dur > 0L && pos > 0L)
- DataStoreHelper.setViewPos(getKey(resumeApp.lastId), pos, dur)
- removeKey(resumeApp.lastId)
- ResultFragment.updateUI()
- }
- }
+ MainActivity.activityResultLauncher = componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
+ val actionUid = getKey("last_click_action") ?: return@registerForActivityResult
+ Log.d(TAG, "Loading action $actionUid result handler")
+ val action = VideoClickActionHolder.getByUniqueId(actionUid) as? OpenInAppAction ?: return@registerForActivityResult
+ action.onResult(act, result.data)
+ removeKey("last_click_action")
+ removeKey("last_opened_id")
+ }
}
// Ask for notification permissions on Android 13
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 3581a3d29..fa54545cf 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -173,7 +173,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
-import com.lagradost.cloudstream3.utils.fcast.FcastManager
+import com.lagradost.cloudstream3.actions.temp.fcast.FcastManager
import com.lagradost.safefile.SafeFile
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -186,120 +186,9 @@ import kotlin.math.abs
import kotlin.math.absoluteValue
import kotlin.system.exitProcess
-//https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
-//https://wiki.videolan.org/Android_Player_Intents/
-
-//https://github.com/mpv-android/mpv-android/blob/0eb3cdc6f1632636b9c30d52ec50e4b017661980/app/src/main/java/is/xyz/mpv/MPVActivity.kt#L904
-//https://mpv-android.github.io/mpv-android/intent.html
-
-// https://www.webvideocaster.com/integrations
-
-//https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225
-
class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCallback {
companion object {
- const val VLC_PACKAGE = "org.videolan.vlc"
- const val MPV_PACKAGE = "is.xyz.mpv"
- const val MPV_YTDL_PACKAGE = "is.xyz.mpv.ytdl"
- const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo"
-
- val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity")
- val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity")
- val MPV_YTDL_COMPONENT = ComponentName(MPV_YTDL_PACKAGE, "$MPV_PACKAGE.MPVActivity")
-
- //TODO REFACTOR AF
- open class ResultResume(
- val packageString: String,
- val action: String = Intent.ACTION_VIEW,
- val position: String? = null,
- val duration: String? = null,
- var launcher: ActivityResultLauncher? = null,
- ) {
- val defaultTime = -1L
-
- val lastId get() = "${packageString}_last_open_id"
- suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) {
- val intent = Intent(action)
-
- if (id != null)
- setKey(lastId, id)
- else
- removeKey(lastId)
-
- intent.setPackage(packageString)
- callback.invoke(intent)
- launcher?.launch(intent)
- }
-
- open fun getPosition(intent: Intent?): Long {
- return defaultTime
- }
-
- open fun getDuration(intent: Intent?): Long {
- return defaultTime
- }
- }
-
- val VLC = object : ResultResume(
- VLC_PACKAGE,
- // Android 13 intent restrictions fucks up specifically launching the VLC player
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- "org.videolan.vlc.player.result"
- } else {
- Intent.ACTION_VIEW
- },
- "extra_position",
- "extra_duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime
- }
- }
-
- val MPV = object : ResultResume(
- MPV_PACKAGE,
- //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
- position = "position",
- duration = "duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
- }
-
- val MPV_YTDL = object : ResultResume(
- MPV_YTDL_PACKAGE,
- //"is.xyz.mpv.ytdl/is.xyz.mpv.MPVActivity.result", // resume not working :pensive:
- position = "position",
- duration = "duration",
- ) {
- override fun getPosition(intent: Intent?): Long {
- return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
-
- override fun getDuration(intent: Intent?): Long {
- return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong()
- ?: defaultTime
- }
- }
-
- val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE)
-
- val resumeApps = arrayOf(
- VLC, MPV, MPV_YTDL, WEB_VIDEO
- )
-
+ var activityResultLauncher: ActivityResultLauncher? = null
const val TAG = "MAINACT"
const val ANIMATED_OUTLINE: Boolean = false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
new file mode 100644
index 000000000..99c1ac38b
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -0,0 +1,134 @@
+package com.lagradost.cloudstream3.actions
+
+import android.app.Activity
+import android.content.ActivityNotFoundException
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+import androidx.core.content.FileProvider
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
+import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
+import com.lagradost.cloudstream3.CommonActivity.showToast
+import com.lagradost.cloudstream3.MainActivity.Companion.activityResultLauncher
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.mvvm.logError
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.ResultFragment
+import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+
+fun updateDurationAndPosition(position: Long, duration: Long) {
+ if (position <= 0 || duration <= 0) return
+ DataStoreHelper.setViewPos(getKey("last_opened_id"), position, duration)
+ ResultFragment.updateUI()
+}
+
+/**
+ * Util method that may be helpful for creating intents for apps that support m3u8 files.
+ * All sources are written to a temporary m3u8 file, which is then sent to the app.
+ */
+fun makeTempM3U8Intent(
+ context: Context,
+ intent: Intent,
+ result: LinkLoadingResult) {
+ intent.apply {
+ addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ }
+
+ val outputDir = context.cacheDir
+
+ if (result.links.size == 1) {
+ intent.setDataAndType(result.links.first().url.toUri(), "video/*")
+ } else {
+ val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
+
+ var text = "#EXTM3U\n#EXT-X-VERSION:3"
+
+ result.links.forEachIndexed { index, link ->
+ text += "\n#EXTINF:$index,${link.name}\n${link.url}"
+ }
+
+ //With subtitles it doesn't work for no reason :(
+ /*for (sub in result.subs) {
+ val normalizedName = sub.name.replace("[^a-zA-Z0-9 ]".toRegex(), "")
+ text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.languageCode}\",URI=\"${sub.url}\""
+ }*/
+
+ text += "\n#EXT-X-ENDLIST"
+
+ outputFile.writeText(text)
+
+ intent.setDataAndType(
+ FileProvider.getUriForFile(
+ context,
+ context.applicationContext.packageName + ".provider",
+ outputFile
+ ), "application/x-mpegURL"
+ )
+ }
+}
+
+abstract class OpenInAppAction(
+ open val appName: UiText,
+ open val packageName: String,
+ private val intentClass: String? = null,
+ private val action: String = Intent.ACTION_VIEW
+): VideoClickAction() {
+ override val name: UiText
+ get() = txt(R.string.episode_action_play_in_format, appName)
+
+ override val isPlayer = true
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = context?.isAppInstalled(packageName) == true
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (context == null) return
+ val intent = Intent(action)
+ intent.setPackage(packageName)
+ if (intentClass != null) {
+ intent.component = ComponentName(packageName, intentClass)
+ }
+ putExtra(context, intent, video, result, index)
+ setKey("last_opened_id", video.id)
+ try {
+ CoroutineScope(Dispatchers.IO).launch {
+ activityResultLauncher?.launch(intent)
+ }
+ } catch (_: ActivityNotFoundException) {
+ showToast(R.string.app_not_found_error, Toast.LENGTH_LONG)
+ } catch (t: Throwable) {
+ logError(t)
+ showToast(t.toString(), Toast.LENGTH_LONG)
+ }
+ }
+
+ /**
+ * Before intent is sent, this function is called to put extra data into the intent.
+ * @see VideoClickAction.runAction
+ * */
+ abstract fun putExtra(context: Context, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+
+ /**
+ * This function is called when the app is opened again after the intent was sent.
+ * You can use it to for example update duration and position.
+ * @see updateDurationAndPosition
+ */
+ abstract fun onResult(activity: Activity, intent: Intent?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
new file mode 100644
index 000000000..f66ed74d9
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -0,0 +1,87 @@
+package com.lagradost.cloudstream3.actions
+
+import android.app.Activity
+import android.content.Context
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.actions.temp.CopyClipboardAction
+import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
+import com.lagradost.cloudstream3.actions.temp.MpvKtPreviewPackage
+import com.lagradost.cloudstream3.actions.temp.MpvPackage
+import com.lagradost.cloudstream3.actions.temp.MpvYTDLPackage
+import com.lagradost.cloudstream3.actions.temp.PlayInBrowserAction
+import com.lagradost.cloudstream3.actions.temp.ViewM3U8Action
+import com.lagradost.cloudstream3.actions.temp.VlcPackage
+import com.lagradost.cloudstream3.actions.temp.WebVideoCastPackage
+import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+import kotlin.reflect.jvm.jvmName
+
+object VideoClickActionHolder {
+ val allVideoClickActions = threadSafeListOf(
+ PlayInBrowserAction(), CopyClipboardAction(),
+ VlcPackage(), ViewM3U8Action(),
+ MpvPackage(), MpvYTDLPackage(),
+ WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage(),
+ FcastAction()
+ )
+
+ init {
+ Log.d("VideoClickActionHolder", "allVideoClickActions: ${allVideoClickActions.map { it.uniqueId() }}")
+ }
+
+ private const val ACTION_ID_OFFSET = 1000
+
+ fun makeOptionMap(activity: Activity?, video: ResultEpisode) = allVideoClickActions
+ // We need to have index before filtering
+ .mapIndexed { id, it -> it to id + ACTION_ID_OFFSET }
+ .filter { it.first.shouldShow(activity, video) }
+ .map { it.first.name to it.second }
+
+
+ fun getActionById(id: Int): VideoClickAction? = allVideoClickActions.getOrNull(id - ACTION_ID_OFFSET)
+
+ fun getByUniqueId(uniqueId: String): VideoClickAction? = allVideoClickActions.firstOrNull { it.uniqueId() == uniqueId }
+
+ fun uniqueIdToId(uniqueId: String?): Int? {
+ if (uniqueId == null) return null
+ return allVideoClickActions
+ .mapIndexed { id, it -> it to id + ACTION_ID_OFFSET }
+ .firstOrNull { it.first.uniqueId() == uniqueId }
+ ?.second
+ }
+
+ fun getPlayers(activity: Activity? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShow(activity, null) }
+}
+
+abstract class VideoClickAction {
+ abstract val name: UiText
+
+ /** if true, the app will show dialog to select source - result.links[index] */
+ open val oneSource : Boolean = false
+
+ /** if true, this action could be selected as default player (one press action) in settings */
+ open val isPlayer: Boolean = false
+
+ /** Which type of sources this action can handle. */
+ open val sourceTypes: Set = ExtractorLinkType.entries.toSet()
+
+ /** Determines which plugin a given provider is from. This is the full path to the plugin. */
+ var sourcePlugin: String? = null
+
+ fun uniqueId() = "$sourcePlugin:${this::class.jvmName}"
+
+ abstract fun shouldShow(context: Context?, video: ResultEpisode?): Boolean
+
+ /**
+ * This function is called when the action is clicked.
+ * @param context The current activity
+ * @param video The episode/movie that was clicked
+ * @param result The result of the link loading, contains video & subtitle links
+ * @param index if oneSource is true, this is the index of the selected source
+ */
+ abstract fun runAction(context: Context?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
new file mode 100644
index 000000000..e054b5ce2
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
@@ -0,0 +1,27 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.content.Context
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
+
+class CopyClipboardAction: VideoClickAction() {
+ override val name = txt("Copy to clipboard")
+
+ override val oneSource = true
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (index == null) return
+ val link = result.links.getOrNull(index) ?: return
+ clipboardHelper(txt(link.name), link.url)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
new file mode 100644
index 000000000..f5ded49b8
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
@@ -0,0 +1,69 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class MpvKtPreviewPackage: MpvKtPackage(
+ appName = "mpvKt Preview",
+ packageName = "live.mehiz.mpvkt.preview",
+)
+
+open class MpvKtPackage(
+ appName: String = "mpvKt",
+ packageName: String = "live.mehiz.mpvkt",
+): OpenInAppAction(
+ appName = txt(appName),
+ packageName = packageName,
+ intentClass = "live.mehiz.mpvkt.ui.player.PlayerActivity"
+) {
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun putExtra(
+ context: Context,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links.getOrNull(index ?: 0) ?: return
+
+ intent.apply {
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ setDataAndType(Uri.parse(link.url), "video/*")
+
+ // m3u8 plays, but changing sources feature is not available
+ // makeTempM3U8Intent(activity, this, result)
+
+ //putExtra("headers", link.headers.flatMap { listOf(it.key, it.value) }.toTypedArray())
+
+ val position = getViewPos(video.id)?.position
+ if (position != null)
+ putExtra("position", position.toInt())
+
+ putExtra("secure_uri", true)
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getIntExtra("position", -1)?.toLong() ?: -1
+ val duration = intent?.getIntExtra("duration", -1)?.toLong() ?: -1
+ updateDurationAndPosition(position, duration)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
new file mode 100644
index 000000000..4c66d0450
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -0,0 +1,61 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import androidx.core.net.toUri
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+// https://github.com/mpv-android/mpv-android/blob/0eb3cdc6f1632636b9c30d52ec50e4b017661980/app/src/main/java/is/xyz/mpv/MPVActivity.kt#L904
+// https://mpv-android.github.io/mpv-android/intent.html
+
+class MpvYTDLPackage : MpvPackage("MPV YTDL", "is.xyz.mpv.ytdl") {
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+}
+
+open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv"): OpenInAppAction(
+ txt(appName),
+ packageName,
+ "is.xyz.mpv.MPVActivity"
+) {
+
+ override fun putExtra(
+ context: Context,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ intent.apply {
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ putExtra("title", video.name)
+
+ makeTempM3U8Intent(context, this, result)
+
+ val position = getViewPos(video.id)?.position
+ if (position != null)
+ putExtra("position", position.toInt())
+
+ putExtra("secure_uri", true)
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getIntExtra("position", -1) ?: -1
+ val duration = intent?.getIntExtra("duration", -1) ?: -1
+ Log.d("MPV", "Position: $position, Duration: $duration")
+ updateDurationAndPosition(position.toLong(), duration.toLong())
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
new file mode 100644
index 000000000..de32bb4b3
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -0,0 +1,44 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.mvvm.logError
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+class PlayInBrowserAction: VideoClickAction() {
+ override val name = txt(R.string.episode_action_play_in_format, "Browser")
+
+ override val oneSource = true
+
+ override val isPlayer = true
+
+ override val sourceTypes: Set = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links.getOrNull(index ?: 0) ?: return
+ try {
+ val i = Intent(Intent.ACTION_VIEW)
+ i.data = Uri.parse(link.url)
+ context?.startActivity(i)
+ } catch (e: Exception) {
+ logError(e)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
new file mode 100644
index 000000000..c14168e96
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
@@ -0,0 +1,30 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.content.Context
+import android.content.Intent
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+
+class ViewM3U8Action: VideoClickAction() {
+ override val name = txt(R.string.episode_action_play_in_format, "m3u8 player")
+
+ override val isPlayer = true
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = true
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ if (context == null) return
+ val i = Intent(Intent.ACTION_VIEW)
+ makeTempM3U8Intent(context, i, result)
+ context.startActivity(i)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
new file mode 100644
index 000000000..ecb37fdc6
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -0,0 +1,68 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
+import com.lagradost.cloudstream3.actions.updateDurationAndPosition
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+
+// https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898
+// https://wiki.videolan.org/Android_Player_Intents/
+
+class VlcPackage: OpenInAppAction(
+ appName = txt("VLC"),
+ packageName = "org.videolan.vlc",
+ intentClass = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ "org.videolan.vlc.gui.video.VideoPlayerActivity"
+ } else {
+ null
+ },
+ action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ "org.videolan.vlc.player.result"
+ } else {
+ Intent.ACTION_VIEW
+ }
+) {
+ override val oneSource = false
+
+ override fun putExtra(
+ context: Context,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+
+ makeTempM3U8Intent(context, intent, result)
+
+ val position = getViewPos(video.id)?.position ?: 0L
+
+ intent.putExtra("from_start", false)
+ intent.putExtra("position", position)
+ intent.putExtra("secure_uri", true)
+ intent.putExtra("title", video.name)
+
+ val subsLang = getKey(SUBTITLE_AUTO_SELECT_KEY) ?: "en"
+ result.subs.firstOrNull {
+ subsLang == it.languageCode
+ }?.let {
+ intent.putExtra("subtitles_location", it.url)
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) {
+ val position = intent?.getLongExtra("extra_position", -1) ?: -1
+ val duration = intent?.getLongExtra("extra_duration", -1) ?: -1
+ Log.d("VLC", "Position: $position, Duration: $duration")
+ updateDurationAndPosition(position, duration)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
new file mode 100644
index 000000000..f8419f63c
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
@@ -0,0 +1,62 @@
+package com.lagradost.cloudstream3.actions.temp
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.core.net.toUri
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.actions.OpenInAppAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+
+// https://www.webvideocaster.com/integrations
+
+class WebVideoCastPackage: OpenInAppAction(
+ txt("Web Video Cast"),
+ "com.instantbits.cast.webvideo"
+) {
+
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun putExtra(
+ context: Context,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links[index ?: 0]
+
+ intent.apply {
+ setDataAndType(Uri.parse(link.url), "video/*")
+
+ val title = video.name ?: video.headerName
+
+ putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
+ putExtra("title", title)
+ video.poster?.let { putExtra("poster", it) }
+ val headers = Bundle().apply {
+ if (link.referer.isNotBlank())
+ putString("Referer", link.referer)
+ putString("User-Agent", USER_AGENT)
+ for ((key, value) in link.headers) {
+ putString(key, value)
+ }
+ }
+ putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
+ putExtra("secure_uri", true)
+ }
+ }
+
+ override fun onResult(activity: Activity, intent: Intent?) = Unit
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
new file mode 100644
index 000000000..c0f92e4df
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -0,0 +1,67 @@
+package com.lagradost.cloudstream3.actions.temp.fcast
+
+import android.content.Context
+import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
+import com.lagradost.cloudstream3.ui.result.ResultEpisode
+import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
+import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
+
+class FcastAction: VideoClickAction() {
+ override val name = txt("Fcast to device")
+
+ override val oneSource = true
+
+ override val sourceTypes = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+ )
+
+ override fun shouldShow(context: Context?, video: ResultEpisode?) = FcastManager.currentDevices.isNotEmpty()
+
+ override fun runAction(
+ context: Context?,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ ) {
+ val link = result.links.getOrNull(index ?: 0) ?: return
+ val devices = FcastManager.currentDevices.toList()
+ context?.getActivity()?.showBottomDialog(
+ devices.map { it.name },
+ -1,
+ txt(R.string.player_settings_select_cast_device).asString(context),
+ false,
+ {}) {
+ val position = getViewPos(video.id)?.position
+ castTo(devices.getOrNull(it), link, position)
+ }
+ }
+
+
+ private fun castTo(device: PublicDeviceInfo?, link: ExtractorLink, position: Long?) {
+ val host = device?.host ?: return
+
+ FcastSession(host).use { session ->
+ session.sendMessage(
+ Opcode.Play,
+ PlayMessage(
+ "video/*",
+ link.url,
+ time = position?.let { it / 1000.0 },
+ headers = mapOf(
+ "referer" to link.referer,
+ "user-agent" to USER_AGENT
+ ) + link.headers
+ )
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
similarity index 98%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
index e7c36a872..78682ca1c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastManager.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
import android.content.Context
import android.net.nsd.NsdManager
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
similarity index 96%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
index 1f33bca43..326d11191 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastSession.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
import android.util.Log
import androidx.annotation.WorkerThread
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
similarity index 95%
rename from app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt
rename to app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
index 61c00d6ed..26f5cec53 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/Packets.kt
@@ -1,4 +1,4 @@
-package com.lagradost.cloudstream3.utils.fcast
+package com.lagradost.cloudstream3.actions.temp.fcast
// See https://gitlab.com/futo-org/fcast/-/wikis/Protocol-version-1
enum class Opcode(val value: Byte) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
index fc8365876..e35ae24b9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/Plugin.kt
@@ -9,6 +9,8 @@ import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.extractorApis
import android.util.Log
import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
const val PLUGIN_TAG = "PluginInstance"
@@ -52,6 +54,18 @@ abstract class Plugin {
extractorApis.add(element)
}
+ /**
+ * Used to register VideoClickAction instances
+ * @param element VideoClickAction you want to register
+ */
+ fun registerVideoClickAction(element: VideoClickAction) {
+ Log.i(PLUGIN_TAG, "Adding ${element.name} VideoClickAction")
+ element.sourcePlugin = this.filename
+ synchronized(VideoClickActionHolder.allVideoClickActions) {
+ VideoClickActionHolder.allVideoClickActions.add(element)
+ }
+ }
+
class Manifest {
@JsonProperty("name")
var name: String? = null
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index c7f416883..8535592d4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -24,6 +24,8 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
+import com.lagradost.cloudstream3.actions.VideoClickAction
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
@@ -583,8 +585,13 @@ object PluginManager {
synchronized(APIHolder.allProviders) {
APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.filename }
}
+
extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.filename }
+ synchronized(VideoClickActionHolder.allVideoClickActions) {
+ VideoClickActionHolder.allVideoClickActions.removeIf { action: VideoClickAction -> action.sourcePlugin == plugin.filename }
+ }
+
classLoaders.values.removeIf { v -> v == plugin }
plugins.remove(absolutePath)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
index 1eaac5056..4b5d680c3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt
@@ -3,10 +3,15 @@ package com.lagradost.cloudstream3.ui
import android.os.Bundle
import android.util.Log
import android.view.Menu
-import android.view.View.*
-import android.widget.*
+import android.view.View.GONE
+import android.view.View.INVISIBLE
+import android.view.View.VISIBLE
+import android.widget.AbsListView
+import android.widget.ArrayAdapter
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ListView
import androidx.appcompat.app.AlertDialog
-import androidx.media3.common.util.UnstableApi
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.kotlinModule
@@ -25,7 +30,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.sortUrls
-import com.lagradost.cloudstream3.ui.player.LoadType
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_CHROMECAST
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.result.ResultEpisode
@@ -298,14 +303,16 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
val isSuccessful = safeApiCall {
generator.generateLinks(
- clearCache = false, type = LoadType.Chromecast,
+ clearCache = false,
+ allowedTypes = LOADTYPE_CHROMECAST,
callback = {
it.first?.let { link ->
currentLinks.add(link)
}
}, subtitleCallback = {
currentSubs.add(it)
- })
+ },
+ isCasting = true)
}
val sortedLinks = sortUrls(currentLinks)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
index c7db7d045..7d3d18ca9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt
@@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSubtitleMimeType
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.SubtitleUtils.cleanDisplayName
import com.lagradost.cloudstream3.utils.SubtitleUtils.isMatchingSubtitle
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadFileInfoAndUpdateSettings
@@ -57,10 +58,11 @@ class DownloadFileGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
val meta = episodes[currentIndex + offset]
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
index ec485f1c8..794dd762d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/ExtractorLinkGenerator.kt
@@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.player
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
class ExtractorLinkGenerator(
private val links: List,
@@ -37,15 +38,15 @@ class ExtractorLinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
subtitles.forEach(subtitleCallback)
- val allowedTypes = type.toSet()
links.forEach {
- if(allowedTypes.contains(it.type)) {
+ if(sourceTypes.contains(it.type)) {
callback.invoke(it to null)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
index 6b8e6ea88..31cf0c70f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt
@@ -3,45 +3,25 @@ package com.lagradost.cloudstream3.ui.player
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
-enum class LoadType {
- Unknown,
- InApp,
- InAppDownload,
- ExternalApp,
- Browser,
- Chromecast,
- Fcast
-}
+val LOADTYPE_INAPP = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_INAPP_DOWNLOAD = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_CHROMECAST = setOf(
+ ExtractorLinkType.VIDEO,
+ ExtractorLinkType.DASH,
+ ExtractorLinkType.M3U8
+)
+
+val LOADTYPE_ALL = ExtractorLinkType.entries.toSet()
-fun LoadType.toSet() : Set {
- return when(this) {
- LoadType.InApp -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.Browser -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.InAppDownload -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.M3U8
- )
- LoadType.ExternalApp, LoadType.Unknown -> ExtractorLinkType.entries.toSet()
- LoadType.Chromecast -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- LoadType.Fcast -> setOf(
- ExtractorLinkType.VIDEO,
- ExtractorLinkType.DASH,
- ExtractorLinkType.M3U8
- )
- }
-}
interface IGenerator {
val hasCache: Boolean
@@ -60,9 +40,10 @@ interface IGenerator {
/* not safe, must use try catch */
suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
offset: Int = 0,
+ isCasting: Boolean = false
): Boolean
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
index 20feae413..109e3137b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/LinkGenerator.kt
@@ -4,6 +4,7 @@ import android.net.Uri
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.loadExtractor
@@ -69,10 +70,11 @@ class LinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ sourceTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean
): Boolean {
links.amap { link ->
if (!extract || !loadExtractor(link.url, referer, {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
index 122eaa975..67cd9de6d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
@@ -15,6 +15,7 @@ import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.EpisodeSkip
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@@ -94,7 +95,7 @@ class PlayerGeneratorViewModel : ViewModel() {
if (generator?.hasCache == true && generator?.hasNext() == true) {
safeApiCall {
generator?.generateLinks(
- type = LoadType.InApp,
+ sourceTypes = LOADTYPE_INAPP,
clearCache = false,
callback = {},
subtitleCallback = {},
@@ -173,7 +174,7 @@ class PlayerGeneratorViewModel : ViewModel() {
}
}
- fun loadLinks(type: LoadType = LoadType.InApp) {
+ fun loadLinks(sourceTypes: Set = LOADTYPE_INAPP) {
Log.i(TAG, "loadLinks")
currentJob?.cancel()
@@ -188,7 +189,7 @@ class PlayerGeneratorViewModel : ViewModel() {
// load more data
_loadingLinks.postValue(Resource.Loading())
val loadingState = safeApiCall {
- generator?.generateLinks(type = type, clearCache = forceClearCache, callback = {
+ generator?.generateLinks(sourceTypes = sourceTypes, clearCache = forceClearCache, callback = {
currentLinks.add(it)
// Clone to prevent ConcurrentModificationException
normalSafeApiCall {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
index 588afbb50..b97ca155b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/RepoLinkGenerator.kt
@@ -1,13 +1,13 @@
package com.lagradost.cloudstream3.ui.player
import android.util.Log
-import androidx.media3.common.util.UnstableApi
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ExtractorLinkType
import kotlin.math.max
import kotlin.math.min
@@ -75,12 +75,12 @@ class RepoLinkGenerator(
override suspend fun generateLinks(
clearCache: Boolean,
- type: LoadType,
+ allowedTypes: Set,
callback: (Pair) -> Unit,
subtitleCallback: (SubtitleData) -> Unit,
- offset: Int
+ offset: Int,
+ isCasting: Boolean,
): Boolean {
- val allowedTypes = type.toSet()
val index = currentIndex
val current = episodes.getOrNull(index + offset) ?: return false
@@ -123,7 +123,7 @@ class RepoLinkGenerator(
val result = APIRepository(
getApiFromNameNull(current.apiName) ?: throw Exception("This provider does not exist")
).loadLinks(current.data,
- isCasting = LoadType.Chromecast == type,
+ isCasting = isCasting,
subtitleCallback = { file ->
val correctFile = PlayerSubtitleHelper.getSubtitleData(file)
if (correctFile.url.isNotEmpty() && !currentSubsUrls.contains(correctFile.url)) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 4cd9cc9ea..2dd8e2ab4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -11,6 +11,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.databinding.ResultEpisodeBinding
import com.lagradost.cloudstream3.databinding.ResultEpisodeLargeBinding
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
@@ -30,9 +31,11 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
+/**
+ * Ids >= 1000 are reserved for VideoClickActions
+ * @see VideoClickActionHolder
+ */
const val ACTION_PLAY_EPISODE_IN_PLAYER = 1
-const val ACTION_PLAY_EPISODE_IN_VLC_PLAYER = 2
-const val ACTION_PLAY_EPISODE_IN_BROWSER = 3
const val ACTION_CHROME_CAST_EPISODE = 4
const val ACTION_CHROME_CAST_MIRROR = 5
@@ -41,7 +44,6 @@ const val ACTION_DOWNLOAD_EPISODE = 6
const val ACTION_DOWNLOAD_MIRROR = 7
const val ACTION_RELOAD_EPISODE = 8
-const val ACTION_COPY_LINK = 9
const val ACTION_SHOW_OPTIONS = 10
@@ -52,12 +54,7 @@ const val ACTION_SHOW_DESCRIPTION = 15
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE = 13
const val ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR = 14
-const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16
-const val ACTION_PLAY_EPISODE_IN_MPV = 17
-const val ACTION_PLAY_EPISODE_IN_MPV_YTDL = 20
-
const val ACTION_MARK_AS_WATCHED = 18
-const val ACTION_FCAST = 19
const val TV_EP_SIZE = 400
@@ -69,22 +66,10 @@ class EpisodeAdapter(
private val downloadClickCallback: (DownloadClickEvent) -> Unit,
) : RecyclerView.Adapter() {
companion object {
- /**
- * @return ACTION_PLAY_EPISODE_IN_PLAYER, ACTION_PLAY_EPISODE_IN_BROWSER or ACTION_PLAY_EPISODE_IN_VLC_PLAYER depending on player settings.
- * See array.xml/player_pref_values
- **/
fun getPlayerAction(context: Context): Int {
-
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
- return when (settingsManager.getInt(context.getString(R.string.player_pref_key), 1)) {
- 1 -> ACTION_PLAY_EPISODE_IN_PLAYER
- 2 -> ACTION_PLAY_EPISODE_IN_VLC_PLAYER
- 3 -> ACTION_PLAY_EPISODE_IN_BROWSER
- 4 -> ACTION_PLAY_EPISODE_IN_WEB_VIDEO
- 5 -> ACTION_PLAY_EPISODE_IN_MPV
- 6 -> ACTION_PLAY_EPISODE_IN_MPV_YTDL
- else -> ACTION_PLAY_EPISODE_IN_PLAYER
- }
+ val playerPref = settingsManager.getString(context.getString(R.string.player_default_key), "")
+ return VideoClickActionHolder.uniqueIdToId(playerPref) ?: ACTION_PLAY_EPISODE_IN_PLAYER
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index a29941d11..b5f83201e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -2,16 +2,11 @@ package com.lagradost.cloudstream3.ui.result
import android.app.Activity
import android.content.*
-import android.net.Uri
-import android.os.Build
-import android.os.Bundle
import android.text.format.Formatter.formatFileSize
import android.util.Log
import android.widget.Toast
import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog
-import androidx.core.content.FileProvider
-import androidx.core.net.toUri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -30,37 +25,28 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie
import com.lagradost.cloudstream3.LoadResponse.Companion.readIdFromString
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.MPV_YTDL_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC_COMPONENT
-import com.lagradost.cloudstream3.MainActivity.Companion.VLC_PACKAGE
-import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO
-import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO_CAST_PACKAGE
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.metaproviders.SyncRedirector
import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.secondsToReadable
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
-import com.lagradost.cloudstream3.syncproviders.providers.SimklApi
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
import com.lagradost.cloudstream3.ui.player.IGenerator
-import com.lagradost.cloudstream3.ui.player.LoadType
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_ALL
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_CHROMECAST
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP
+import com.lagradost.cloudstream3.ui.player.LOADTYPE_INAPP_DOWNLOAD
import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator
import com.lagradost.cloudstream3.ui.player.SubtitleData
import com.lagradost.cloudstream3.ui.result.EpisodeAdapter.Companion.getPlayerAction
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppContextUtils.getNameFull
-import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
import com.lagradost.cloudstream3.utils.AppContextUtils.isConnectedToChromecast
import com.lagradost.cloudstream3.utils.AppContextUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.AppContextUtils.sortSubs
@@ -69,6 +55,7 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.Coroutines.ioWorkSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllFavorites
@@ -96,12 +83,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
import com.lagradost.cloudstream3.utils.UIHelper.navigate
-import com.lagradost.cloudstream3.utils.fcast.FcastManager
-import com.lagradost.cloudstream3.utils.fcast.FcastSession
-import com.lagradost.cloudstream3.utils.fcast.Opcode
-import com.lagradost.cloudstream3.utils.fcast.PlayMessage
import kotlinx.coroutines.*
-import java.io.File
import java.util.concurrent.TimeUnit
/** This starts at 1 */
@@ -803,7 +785,7 @@ class ResultViewModel2 : ViewModel() {
val generator = RepoLinkGenerator(listOf(episode))
val currentLinks = mutableSetOf()
val currentSubs = mutableSetOf()
- generator.generateLinks(clearCache = false, LoadType.Chromecast, callback = {
+ generator.generateLinks(clearCache = false, allowedTypes = LOADTYPE_INAPP_DOWNLOAD, callback = {
it.first?.let { link ->
currentLinks.add(link)
}
@@ -954,7 +936,7 @@ class ResultViewModel2 : ViewModel() {
isVisible: Boolean = true
) {
if (activity == null) return
- loadLinks(result, isVisible = isVisible, LoadType.Chromecast) { data ->
+ loadLinks(result, isVisible = isVisible, sourceTypes = LOADTYPE_CHROMECAST, isCasting = true) { data ->
startChromecast(activity, result, data.links, data.subs, 0)
}
}
@@ -1264,7 +1246,7 @@ class ResultViewModel2 : ViewModel() {
_loadedLinks.postValue(null)
}
- private fun postPopup(text: UiText, options: List, callback: suspend (Int?) -> Unit) {
+ fun postPopup(text: UiText, options: List, callback: suspend (Int?) -> Unit) {
_selectPopup.postValue(
SelectPopup.SelectText(
text,
@@ -1300,8 +1282,9 @@ class ResultViewModel2 : ViewModel() {
private fun loadLinks(
result: ResultEpisode,
isVisible: Boolean,
- type: LoadType,
+ sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
+ isCasting: Boolean = false,
work: suspend (CoroutineScope.(LinkLoadingResult) -> Unit)
) {
currentLoadLinkJob?.cancel()
@@ -1309,8 +1292,9 @@ class ResultViewModel2 : ViewModel() {
val links = loadLinks(
result,
isVisible = isVisible,
- type = type,
- clearCache = clearCache
+ sourceTypes = sourceTypes,
+ clearCache = clearCache,
+ isCasting = isCasting
)
if (!this.isActive) return@ioSafe
work(links)
@@ -1320,11 +1304,12 @@ class ResultViewModel2 : ViewModel() {
private var currentLoadLinkJob: Job? = null
private fun acquireSingleLink(
result: ResultEpisode,
- type: LoadType,
+ sourceTypes: Set,
text: UiText,
- callback: (Pair) -> Unit,
+ isCasting: Boolean = false,
+ callback: (Pair) -> Unit
) {
- loadLinks(result, isVisible = true, type) { links ->
+ loadLinks(result, isVisible = true, sourceTypes, isCasting = isCasting) { links ->
// Could not find a better way to do this
val context = AcraApplication.context
postPopup(
@@ -1344,7 +1329,7 @@ class ResultViewModel2 : ViewModel() {
text: UiText,
callback: (Pair) -> Unit,
) {
- loadLinks(result, isVisible = true, type = LoadType.Unknown) { links ->
+ loadLinks(result, isVisible = true) { links ->
postPopup(
text,
links.subs.map { txt(it.name) })
@@ -1357,8 +1342,9 @@ class ResultViewModel2 : ViewModel() {
private suspend fun CoroutineScope.loadLinks(
result: ResultEpisode,
isVisible: Boolean,
- type: LoadType,
+ sourceTypes: Set = LOADTYPE_ALL,
clearCache: Boolean = false,
+ isCasting: Boolean = false
): LinkLoadingResult {
val tempGenerator = RepoLinkGenerator(listOf(result))
@@ -1371,15 +1357,19 @@ class ResultViewModel2 : ViewModel() {
}
try {
updatePage()
- tempGenerator.generateLinks(clearCache, type, { (link, _) ->
- if (link != null) {
- links += link
- updatePage()
- }
- }, { sub ->
+ tempGenerator.generateLinks(clearCache,
+ allowedTypes = sourceTypes,
+ callback = { (link, _) ->
+ if (link != null) {
+ links += link
+ updatePage()
+ }
+ },
+ subtitleCallback = { sub ->
subs += sub
updatePage()
- })
+ },
+ isCasting = isCasting)
} catch (e: Exception) {
logError(e)
} finally {
@@ -1389,185 +1379,11 @@ class ResultViewModel2 : ViewModel() {
return LinkLoadingResult(sortUrls(links), sortSubs(subs))
}
- private fun launchActivity(
- activity: Activity?,
- resumeApp: MainActivity.Companion.ResultResume,
- id: Int? = null,
- work: suspend (Intent.(Activity) -> Unit)
- ): Job? {
- val act = activity ?: return null
- return CoroutineScope(Dispatchers.IO).launch {
- try {
- resumeApp.launch(id) {
- work(act)
- }
- } catch (t: Throwable) {
- logError(t)
- main {
- if (t is ActivityNotFoundException) {
- showToast(txt(R.string.app_not_found_error), Toast.LENGTH_LONG)
- } else {
- showToast(t.toString(), Toast.LENGTH_LONG)
- }
- }
- }
- }
- }
-
- private fun playInWebVideo(
- activity: Activity?,
- link: ExtractorLink,
- title: String?,
- posterUrl: String?,
- subtitles: List
- ) = launchActivity(activity, WEB_VIDEO) {
- setDataAndType(Uri.parse(link.url), "video/*")
-
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- title?.let { putExtra("title", title) }
- posterUrl?.let { putExtra("poster", posterUrl) }
- val headers = Bundle().apply {
- if (link.referer.isNotBlank())
- putString("Referer", link.referer)
- putString("User-Agent", USER_AGENT)
- for ((key, value) in link.headers) {
- putString(key, value)
- }
- }
- putExtra("android.media.intent.extra.HTTP_HEADERS", headers)
- putExtra("secure_uri", true)
- }
-
- private fun playWithMpv(
- activity: Activity?,
- id: Int,
- link: ExtractorLink,
- subtitles: List,
- resume: Boolean = true,
- ) = launchActivity(activity, MPV, id) {
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", subtitles.map { it.name }.toTypedArray())
- putExtra("subs.filename", subtitles.map { it.name }.toTypedArray())
- setDataAndType(Uri.parse(link.url), "video/*")
- component = MPV_COMPONENT
- putExtra("secure_uri", true)
- putExtra("return_result", true)
- val position = getViewPos(id)?.position
- if (resume && position != null)
- putExtra("position", position.toInt())
- }
-
- private fun playWithMpvYtdl(
- activity: Activity?,
- id: Int,
- link: ExtractorLink,
- subtitles: List,
- resume: Boolean = true,
- ) = launchActivity(activity, MPV_YTDL, id) {
- putExtra("subs", subtitles.map { it.url.toUri() }.toTypedArray())
- putExtra("subs.name", subtitles.map { it.name }.toTypedArray())
- putExtra("subs.filename", subtitles.map { it.name }.toTypedArray())
- setDataAndType(Uri.parse(link.url), "video/*")
- component = MPV_YTDL_COMPONENT
- putExtra("secure_uri", true)
- putExtra("return_result", true)
- val position = getViewPos(id)?.position
- if (resume && position != null)
- putExtra("position", position.toInt())
- }
-
- // https://wiki.videolan.org/Android_Player_Intents/
- private fun playWithVlc(
- activity: Activity?,
- data: LinkLoadingResult,
- id: Int,
- resume: Boolean = true,
- // if it is only a single link then resume works correctly
- singleFile: Boolean? = null
- ) = launchActivity(activity, VLC, id) { act ->
- addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
-
- val outputDir = act.cacheDir
-
- if (singleFile ?: (data.links.size == 1)) {
- setDataAndType(data.links.first().url.toUri(), "video/*")
- } else {
- val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
-
- var text = "#EXTM3U"
-
- // With subtitles it doesn't work for no reason :(
-// for (sub in data.subs) {
-// text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${sub.name}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.name}\",URI=\"${sub.url}\""
-// }
- for (link in data.links) {
- text += "\n#EXTINF:, ${link.name}\n${link.url}"
- }
- outputFile.writeText(text)
-
- setDataAndType(
- FileProvider.getUriForFile(
- act,
- act.applicationContext.packageName + ".provider",
- outputFile
- ), "video/*"
- )
- }
-
- val position = if (resume) {
- getViewPos(id)?.position ?: 0L
- } else {
- 1L
- }
-
- // Component no longer safe to use in A13 for VLC
- // https://code.videolan.org/videolan/vlc-android/-/issues/2776
- // This will likely need to be updated once VLC fixes their documentation.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- component = VLC_COMPONENT
- }
-
- putExtra("from_start", !resume)
- putExtra("position", position)
- }
-
-
fun handleAction(click: EpisodeClickEvent) =
viewModelScope.launchSafe {
handleEpisodeClickEvent(click)
}
- data class ExternalApp(
- val packageString: String,
- val name: Int,
- val action: Int,
- )
-
- private val apps = listOf(
- ExternalApp(
- VLC_PACKAGE,
- R.string.player_settings_play_in_vlc,
- ACTION_PLAY_EPISODE_IN_VLC_PLAYER
- ), ExternalApp(
- WEB_VIDEO_CAST_PACKAGE,
- R.string.player_settings_play_in_web,
- ACTION_PLAY_EPISODE_IN_WEB_VIDEO
- ),
- ExternalApp(
- MPV_PACKAGE,
- R.string.player_settings_play_in_mpv,
- ACTION_PLAY_EPISODE_IN_MPV
- ),
- ExternalApp(
- MPV_YTDL_PACKAGE,
- R.string.player_settings_play_in_mpvytdl,
- ACTION_PLAY_EPISODE_IN_MPV_YTDL
- )
- )
-
fun releaseEpisodeSynopsis() {
_episodeSynopsis.postValue(null)
}
@@ -1576,6 +1392,7 @@ class ResultViewModel2 : ViewModel() {
when (click.action) {
ACTION_SHOW_OPTIONS -> {
val options = mutableListOf>()
+
if (activity?.isConnectedToChromecast() == true) {
options.addAll(
listOf(
@@ -1585,29 +1402,10 @@ class ResultViewModel2 : ViewModel() {
)
}
- if (FcastManager.currentDevices.isNotEmpty()) {
- options.add(
- txt(R.string.player_settings_play_in_fcast) to ACTION_FCAST
- )
- }
-
options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER)
- for (app in apps) {
- if (activity?.isAppInstalled(app.packageString) == true) {
- options.add(
- txt(
- R.string.episode_action_play_in_format,
- txt(app.name)
- ) to app.action
- )
- }
- }
-
options.addAll(
listOf(
- txt(R.string.episode_action_play_in_browser) to ACTION_PLAY_EPISODE_IN_BROWSER,
- txt(R.string.episode_action_copy_link) to ACTION_COPY_LINK,
txt(R.string.episode_action_auto_download) to ACTION_DOWNLOAD_EPISODE,
txt(R.string.episode_action_download_mirror) to ACTION_DOWNLOAD_MIRROR,
txt(R.string.episode_action_download_subtitle) to ACTION_DOWNLOAD_EPISODE_SUBTITLE_MIRROR,
@@ -1615,6 +1413,10 @@ class ResultViewModel2 : ViewModel() {
)
)
+ options.addAll(
+ VideoClickActionHolder.makeOptionMap(activity, click.data)
+ )
+
// Do not add mark as watched on movies
if (!listOf(TvType.Movie, TvType.AnimeMovie).contains(click.data.tvType)) {
val isWatched =
@@ -1716,7 +1518,7 @@ class ResultViewModel2 : ViewModel() {
val response = currentResponse ?: return
acquireSingleLink(
click.data,
- LoadType.InAppDownload,
+ LOADTYPE_INAPP_DOWNLOAD,
txt(R.string.episode_action_download_mirror)
) { (result, index) ->
ioSafe {
@@ -1746,7 +1548,7 @@ class ResultViewModel2 : ViewModel() {
loadLinks(
click.data,
isVisible = false,
- type = LoadType.InApp,
+ LOADTYPE_INAPP,
clearCache = true
)
}
@@ -1759,139 +1561,18 @@ class ResultViewModel2 : ViewModel() {
ACTION_CHROME_CAST_MIRROR -> {
acquireSingleLink(
click.data,
- LoadType.Chromecast,
- txt(R.string.episode_action_chromecast_mirror)
+ LOADTYPE_CHROMECAST,
+ txt(R.string.episode_action_chromecast_mirror),
+ isCasting = true
) { (result, index) ->
startChromecast(activity, click.data, result.links, result.subs, index)
}
}
- ACTION_FCAST -> {
- val devices = FcastManager.currentDevices.toList()
- postPopup(
- txt(R.string.player_settings_select_cast_device),
- devices.map { txt(it.name) }) { index ->
- if (index == null) return@postPopup
- val device = devices.getOrNull(index)
-
- acquireSingleLink(
- click.data,
- LoadType.Fcast,
- txt(R.string.episode_action_cast_mirror)
- ) { (result, index) ->
- val host = device?.host ?: return@acquireSingleLink
- val link = result.links.getOrNull(index) ?: return@acquireSingleLink
-
- FcastSession(host).use { session ->
- session.sendMessage(
- Opcode.Play,
- PlayMessage(
- link.type.getMimeType(),
- link.url,
- headers = mapOf(
- "referer" to link.referer,
- "user-agent" to USER_AGENT
- ) + link.headers
- )
- )
- }
- }
- }
- }
-
- ACTION_PLAY_EPISODE_IN_BROWSER -> acquireSingleLink(
- click.data,
- LoadType.Browser,
- txt(R.string.episode_action_play_in_browser)
- ) { (result, index) ->
- try {
- val i = Intent(Intent.ACTION_VIEW)
- i.data = Uri.parse(result.links[index].url)
- activity?.startActivity(i)
- } catch (e: Exception) {
- logError(e)
- }
- }
-
- ACTION_COPY_LINK -> {
- acquireSingleLink(
- click.data,
- LoadType.ExternalApp,
- txt(R.string.episode_action_copy_link)
- ) { (result, index) ->
- val link = result.links[index]
- clipboardHelper(txt(link.name), link.url)
- }
- }
-
ACTION_CHROME_CAST_EPISODE -> {
startChromecast(activity, click.data)
}
- ACTION_PLAY_EPISODE_IN_VLC_PLAYER -> {
- loadLinks(click.data, isVisible = true, LoadType.ExternalApp) { links ->
- if (links.links.isEmpty()) {
- showToast(R.string.no_links_found_toast, Toast.LENGTH_SHORT)
- return@loadLinks
- }
-
- playWithVlc(
- activity,
- links,
- click.data.id
- )
- }
- }
-
- ACTION_PLAY_EPISODE_IN_WEB_VIDEO -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_web)
- )
- ) { (result, index) ->
- playInWebVideo(
- activity,
- result.links[index],
- click.data.name ?: click.data.headerName,
- click.data.poster,
- result.subs
- )
- }
-
- ACTION_PLAY_EPISODE_IN_MPV -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_mpv)
- )
- ) { (result, index) ->
- playWithMpv(
- activity,
- click.data.id,
- result.links[index],
- result.subs
- )
- }
-
- ACTION_PLAY_EPISODE_IN_MPV_YTDL -> acquireSingleLink(
- click.data,
- LoadType.Chromecast,
- txt(
- R.string.episode_action_play_in_format,
- txt(R.string.player_settings_play_in_mpvytdl)
- )
- ) { (result, index) ->
- playWithMpvYtdl(
- activity,
- click.data.id,
- result.links[index],
- result.subs
- )
- }
-
ACTION_PLAY_EPISODE_IN_PLAYER -> {
val data = currentResponse?.syncData?.toList() ?: emptyList()
val list =
@@ -1907,7 +1588,7 @@ class ResultViewModel2 : ViewModel() {
if (currentResponse?.type == TvType.CustomMedia) {
generator?.generateLinks(
clearCache = true,
- LoadType.Unknown,
+ LOADTYPE_ALL,
callback = {},
subtitleCallback = {})
} else {
@@ -1933,6 +1614,35 @@ class ResultViewModel2 : ViewModel() {
// Kinda dirty to reload all episodes :(
reloadEpisodes()
}
+
+ else -> {
+ val action = VideoClickActionHolder.getActionById(click.action) ?: return
+
+ activity?.setKey("last_click_action", action.uniqueId())
+ if (action.oneSource) {
+ acquireSingleLink(
+ click.data,
+ action.sourceTypes,
+ action.name
+ ) { (result, index) ->
+ action.runAction(
+ activity,
+ click.data,
+ result,
+ index
+ )
+ }
+ } else {
+ loadLinks(click.data, isVisible = true, action.sourceTypes) { links ->
+ action.runAction(
+ activity,
+ click.data,
+ links,
+ null
+ )
+ }
+ }
+ }
}
}
@@ -2040,7 +1750,7 @@ class ResultViewModel2 : ViewModel() {
isResponseRequired = false
)
if (map.isNullOrEmpty()) return@argamap
- updateEpisodes = DubStatus.values().map { dubStatus ->
+ updateEpisodes = DubStatus.entries.map { dubStatus ->
val current =
this.episodes[dubStatus]?.mapIndexed { index, episode ->
episode.apply {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
index 1753032ac..17580236f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt
@@ -6,6 +6,7 @@ import android.view.View
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
@@ -155,10 +156,17 @@ class SettingsPlayer : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
- getPref(R.string.player_pref_key)?.setOnPreferenceClickListener {
- val prefNames = resources.getStringArray(R.array.player_pref_names)
- val prefValues = resources.getIntArray(R.array.player_pref_values)
- val current = settingsManager.getInt(getString(R.string.player_pref_key), 1)
+ getPref(R.string.player_default_key)?.setOnPreferenceClickListener {
+ val players = VideoClickActionHolder.getPlayers(activity)
+ val prefNames = buildList {
+ add(getString(R.string.player_settings_play_in_app))
+ addAll(players.map { it.name.asStringNull(activity) ?: it.javaClass.simpleName })
+ }
+ val prefValues = buildList {
+ add("")
+ addAll(players.map { it.uniqueId() })
+ }
+ val current = settingsManager.getString(getString(R.string.player_default_key), "") ?: ""
activity?.showBottomDialog(
prefNames.toList(),
@@ -166,7 +174,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
getString(R.string.player_pref),
true,
{}) {
- settingsManager.edit().putInt(getString(R.string.player_pref_key), prefValues[it]).apply()
+ settingsManager.edit().putString(getString(R.string.player_default_key), prefValues[it]).apply()
}
return@setOnPreferenceClickListener true
}
diff --git a/app/src/main/res/values-ajp/strings.xml b/app/src/main/res/values-ajp/strings.xml
index 6896cf2af..0b30cebac 100644
--- a/app/src/main/res/values-ajp/strings.xml
+++ b/app/src/main/res/values-ajp/strings.xml
@@ -221,9 +221,7 @@
بسبِب أعطال إزا نحط على مستوى عالي كتير على الأجهزة يللي م بتساع كتير، متل تلفزيون \"أندرويد\".
شي غير
أفي هيدا التجديد
- نسوخ الرابط
مَشي بال آپ
- مشي بمتصفح الويب
مفيد لتجاوز المنع من مزود خدمة الإنترنت
مسلسل
غير الحجم
@@ -390,10 +388,8 @@
م قدرنا ننزل الإصدار الجديد تبع الآپ
المؤلفين
إضافة
- كاست ڤيديو ع الوَب
معقول يكون موجود أصلًا
مشتركينلو
- متصفح الوَب
كل اللغات
دايمًا كتوب ب أحرف كاپيتال، A بدل a
مشغل الڤيديو المفضل
@@ -422,7 +418,6 @@
خلصت
محي السجل
تجَدَد (من قديم للجديد)
- \"ڤي أل سي ميديا پلاير\"
كاميرا
وَب
أبجديًا (من الياء للألف)
@@ -504,7 +499,6 @@
مزامنة
شوفو معلومات عن المشكلة
مدعوم
- \"أم پي ڤي\"
ظبّط وقت الترجمة
افتتاح مختلط
مقطع دعائي
@@ -627,7 +621,6 @@
رح ينزل ب %s
الحلقة ال %2$d من الجزء ال%1$d رح تنزل ب
كاست مراية
- إف كاست
نقي جهاز الكاست
ويكي \"كلود ستريم\"
أكونتات
@@ -669,6 +662,5 @@
\n%s
صورة زغيرة مع التقريب وال تبعيد
بت حط صورة زغير من الڤيديو إنت و عم بت قرب أو ترجع بال ڤيديو
- MPV YTDL
بعد مش معمول لود لولا ترجمة
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 25118d963..56bb4b4a0 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -233,8 +233,6 @@
مرآة كروم كاست
تشغيل في التطبيق
%s تشغيل في
- تشغيل في الويب
- نسخ الرابط
التحميل التلقائي
تحميل بجودات مختلفة
إعادة تحميل الروابط
@@ -464,10 +462,6 @@
فتح(تشغيل)
%1$s %2$d%3$s
المكونات الإضافية المحدثة %d
- VLC
- MPV
- اسقاط فيديو الويب
- متصفح الإنترنت
تخطي %s
الافتتاح
النهاية
@@ -653,7 +647,6 @@
قادم خلال %s
سيتم إصدار الحلقة %1$d من الموسم %2$d في
مرآة البث
- بث ف
حدد جهاز البث
CloudStream ويكي
إعدادات الأمان
@@ -695,6 +688,5 @@
\n%s
معاينة شريط البحث
تمكين معاينة الصورة المصغرة على شريط البحث
- MPV YTDL
لم يتم تحميل أي ترجمات بعد
\ No newline at end of file
diff --git a/app/src/main/res/values-ars/strings.xml b/app/src/main/res/values-ars/strings.xml
index 873c55bf3..d1efdbbc5 100644
--- a/app/src/main/res/values-ars/strings.xml
+++ b/app/src/main/res/values-ars/strings.xml
@@ -311,11 +311,9 @@
اخرون
تخطي هذا التحديث
.قد يتسبب في تأخير التحديثات لبضعة أيام .jsDelivr باستخدام GitHubيتجاوز حظر
- انسخ الرابط
الدرامات الآسيوية
في قائمة الانتظار
افتح في التطبيق
- افتح في المتصفح
مفيد لتجاوز حجب مزودي خدمة الإنترنت
مسلسل
تقييم
diff --git a/app/src/main/res/values-as/strings.xml b/app/src/main/res/values-as/strings.xml
index fc50d2d02..2fdd469b4 100644
--- a/app/src/main/res/values-as/strings.xml
+++ b/app/src/main/res/values-as/strings.xml
@@ -89,7 +89,6 @@
সম্পূৰ্ণ
সৰ্বজনীন তালিকা
বন্ধ কৰক
- VLC
বেটাৰী অপ্টিমাইজেচন নিষ্ক্ৰিয় কৰক
সদস্যতা গ্ৰহণ কৰা
গুণসমূহ
@@ -302,8 +301,6 @@
কাষ্ট মিৰৰ
ডাব ছপা
প্লে %s ত
- ব্ৰাউজাৰত প্লে কৰক
- লিংক কপি কৰক
স্বয়ংক্ৰিয় ডাউনলোড
ডাউনলোড মিৰৰ
সাব ছপা
@@ -498,10 +495,6 @@
HLS প্লেলিস্ট
পছন্দৰ ভিডিঅ\' প্লেয়াৰ
আভ্যন্তৰীণ প্লেয়াৰ
- MPV
- ৱেব ভিডিঅ\' কাষ্ট
- Fcast
- ৱেব ব্ৰাউজাৰ
কাষ্ট ডিভাইচ চয়ন কৰক
মিশ্ৰিত সমাপ্তি
মিশ্ৰিত উদ্ঘাটনী
@@ -650,7 +643,6 @@
ডিভাইচ পিন ক\'ড পোৱা নাই, স্থানীয় প্ৰমাণীকৰণ চেষ্টা কৰক
পিন ক\'ডৰ মেয়াদ শেষ হৈছে!
ক\'ড মেয়াদ শেষ হব %1$dm %2$ds
- MPV YTDL
সীকবৰ প্ৰিভিউ
সীকবৰত প্ৰিভিউ থাম্বনেইল সক্ৰিয় কৰক
আৰম্ভৰ পৰা প্লে কৰক
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 3579c8353..218fa5e57 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -238,8 +238,6 @@
Chromecast огледало
Пусни в приложението
Пусни в %s
- Пусни в браузър
- Копирай връзка
Автоматично изтегляне
Изтегляне на огледало
Презареждане на връзки
@@ -443,10 +441,6 @@
HLS плейлист
Предпочитан видео плеър
Вътрешен плеър
- VLC
- MPV
- Уеб видео предаване
- Уеб браузър
Приложението не е намерено
Автоматично изтегли добавки
Всички езици
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index c5ed86514..f1bc8f82a 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -230,7 +230,6 @@
প্লাগইন ডাউনলোড ফিল্টার করতে মোড নির্বাচন করুন
লিঙ্ক পুনরায় লোড হয়েছে
সুইচ অ্যাকাউন্ট
- ব্রাউজারে প্লে করুন
দাবিত্যাগ
এশিয়ান ড্রামা
সোর্স
@@ -274,7 +273,6 @@
টরেন্ট
এপিসোড ক্রোমকাস্ট করুন
প্লে হচ্ছে %s সময়ের মধ্যে
- লিঙ্ক কপি করুন
স্বয়ংক্রিয় ডাউনলোড
টাইটেল
প্লেয়ার দেখা যাচ্ছে - সিকের পরিমাণ
diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml
index 4c29a99e7..55e1ce180 100644
--- a/app/src/main/res/values-bp/strings.xml
+++ b/app/src/main/res/values-bp/strings.xml
@@ -235,8 +235,6 @@
Alternativa pelo Chromecast
Assistir no App
Assistir no %s
- Assistir no navegador
- Copiar link
Auto download
Baixar por servidor alternativo
Recarregar links
@@ -538,16 +536,13 @@
Começar
Suportado
Status
- MPV
Abrindo mistura
- VLC
Reinicie o aplicativo para ver as alterações.
Visualização info de crash
Faixas de áudio
Adicionado em (novo para antigo)
Faixas de video
Legendas
- Navegador
18+
Links
Funcionalidades do Player
@@ -563,7 +558,6 @@
Vídeo
Android TV
Wi-Fi
- Lista de videos da web
A interface de usuário não foi gerada corretamente. Isto se trata de um bug importante e deve ser reportado imediatamente %s
Características da interface de usuário
Provedor de teste
@@ -642,7 +636,6 @@
Redefinir
Próximos em %s
Temporada %1$d Episódio %2$d será lançado em
- Fcast
Selecione o dispositivo de transmissão
Espelhar transmissão
CloudStream Wiki
@@ -685,6 +678,5 @@
Desmarcar todos
Ativar visualização de miniatura na barra de busca
Visualização da barra de busca
- MPV + YTDL
Ainda não há legendas carregadas
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 04b4b213b..ba03b05fb 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -223,8 +223,6 @@
Chromecast jako zrcadlo
Přehrát v aplikace
Přehrát ve %s
- Přehrát v prohlížeči
- Zkopírovat odkaz
Automaticky stáhnout
Zrcadlo stahování
Obnovit odkazy
@@ -400,8 +398,6 @@
Veřejný seznam
Velká písmena u všech titulků
Playlist HLS
- MPV
- Webové vysílání videa
Aplikace nenalezena
Přeskočit %s
Úvod
@@ -451,13 +447,11 @@
Popis
Stav
Nejprve nainstalujte rozšíření
- VLC
Smíšený konec
Jazyk
Interní přehrávač
Rekapitulace
Vymazat historii
- Webový prohlížeč
Všechny jazyky
Smíšený úvod
Poděkování
@@ -645,7 +639,6 @@
Vychází %s
Epizoda %2$d ze série %1$d bude vydána za
Vysílat zrcadlení
- Fcast
Vyberte zařízení k vysílání
CloudStream Wiki
Zabezpečení
@@ -687,6 +680,5 @@
Odstranit (%1$d | %2$s)
Náhled v liště přehrávače
Povolit náhled miniatur na liště přehrávače
- MPV YTDL
Zatím nenačteny žádné titulky
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index c9656d9de..6cf8ff3bd 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -241,8 +241,6 @@
Downloadfehler, bitte überprüfen sie die Speicherberechtigungen
Chromecast-Episode
In %s wiedergeben
- In Browser wiedergeben
- Link kopieren
Auto-Download
Alternativer Download
Links neu laden
@@ -437,10 +435,6 @@
HLS-Playlist
Bevorzugter Videoplayer
Interner Player
- VLC
- MPV
- Web Video Cast
- Browser
App nicht gefunden
Alle Sprachen
Überspringen %s
@@ -618,7 +612,6 @@
hide_player_control_names_key
Staffel %1$d Episode %2$d wird veröffentlicht in
Wird veröffentlicht in %s
- Fcast
Sicherheit
Konten
Repository öffnen
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index e22182d23..268ea7bd1 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -194,8 +194,6 @@
Chromecast επεισόδιο
Αναπαραγωγή εντός της εφαρμογής
Αναπαραγωγή σε %s
- Αναπαραγωγή στον περιηγητή
- Αντιγραφή συνδέσμου
Αυτόματη λήψη
Λήψη mirror
Επαναφόρτωση συνδέσμων
@@ -359,10 +357,6 @@
HLS Playlist
Προτεινόμενο πρόγραμμα αναπαραγωγής
Ενσωματωμένο πρόγραμμα αναπαραγωγής
- VLC
- MPV
- Web Video Cast
- Περιηγητής
Η εφαρμογή δεν βρέθηκε
%1$s Επ %2$d
Το επεισόδιο %d θα κυκλοφορήσει σε
@@ -580,7 +574,6 @@
Δευτερόλεπτα Σκιπ όταν φαίνεται ο αναπαραγωγέας (πλειερ)
Δοκιμή όλων των παροχών
Αυτό το τεστ προορίζεται μόνο για τους προγραμματιστές και δε επαληθείει ούτε απορρίπτει την λειτουργία οποιουδήποτε παρόχου.
- Fcast
Επιλογή συσκευής για αναμετάδοση
Πρόβλημα στην πρόσβαση στο Clipboard, Παρακαλώ προσπαθήστε ξανά.
Πρόβλημα στην αντιγραφή , Παρακαλούμε αντιγράψτε το logcat και επικοινωνήστε με την υποστήριξη.
diff --git a/app/src/main/res/values-es/array.xml b/app/src/main/res/values-es/array.xml
index eb197f43e..fddd832a5 100644
--- a/app/src/main/res/values-es/array.xml
+++ b/app/src/main/res/values-es/array.xml
@@ -152,32 +152,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 155c5f746..a0c4587f5 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -57,11 +57,7 @@
Cantidad de búsquedas del reproductor (segundos)
Use el brillo del sistema en el reproductor de la app en lugar de una superposición oscura
Resolución del reproductor de video
- MPV
Reproductor
- VLC
- Web Video Cast
- Navegador Web
Iniciar el siguiente episodio cuando el actual termine
Omitir Intro
Apertura
@@ -78,9 +74,7 @@
Actualizar progreso de lo visto
Duplicar en Chromecast
No se encontraron Episodios
- Reproducir en Navegador
Reproducir en %s
- Copiar enlace
Descarga automática
Descargar desde servidor alternativo
Recargar enlaces
@@ -621,7 +615,6 @@
Próximamente en %s
La temporada %1$d y el episodio %2$d se estrenarán en
Seleccionar el dispositivo para transmitir
- Fcast
Espejo de transmisión
Wiki de CloudStream
Seguridad
@@ -663,6 +656,5 @@
\n%s
Activar la previsualización para las miniaturas en la barra de búsqueda
Previsualización de Seekbar
- MPV YTDL
Aún no hay subtítulos cargados
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 0483cb2ea..5b3f09120 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -111,8 +111,6 @@
Miroir Chromecast
Lecture dans l\'application
Lecture dans %s
- Lecture dans le navigateur
- Copier le lien
Téléchargement automatique
Télécharger depuis le miroir
Recharger les liens
@@ -140,7 +138,7 @@
DNS avec HTTPS
Afficher les animés en Anglais (Dub) / sous-titrés
Disposition en mode téléphone
- %1$s Episode %2$d
+ episode_action_copy_link
Note : %.1f
Zoom
Adapter à l\'écran
@@ -430,12 +428,10 @@
Installer l\'extension d\'abord
Playlist HLS
Lecteur vidéo préféré
- VLC
Fin mitigée
Introduction mitigée
Installation de la mise a jour de l\'application…
Impossible d\'installer la nouvelle version de l\'application
- Navigateur Web
Certains téléphones ne supporte pas le nouvel installateur d\'application. Essayez l\'option de l\'ancien installateur si les mises-à-jour ne s\'installe pas.
Précédent
Ignorer la configuration
@@ -456,7 +452,6 @@
Lecteur interne
Application introuvable
Trop de texte. Impossible de sauvegarder dans le presse papier.
- MPV
Installateur de paquet
plugins
Cela supprimera également tous les plugins du repository
@@ -466,7 +461,6 @@
Langage
Afficher les popups skip pour les intro / fins
Ancienne méthode d\'installation
- Web Video Cast
Liens
Gestes
Fonctionnalités du lecteur
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index eed5e44aa..4457b0ec4 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -114,8 +114,6 @@
क्रोमकास्ट मिरर
एप्प में चलाएं
%s में चलाएं
- Browser में चलाएं
- लिंक कॉपी करें
डाउनलोड करें
मिरर डाउनलोड
लिंक दोबारा लोड करें
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 44f1bd5b9..dcacbeee3 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -253,8 +253,6 @@
Chromecast mirror
Pokreni u aplikaciji
Pokreni u %s
- Pokreni u pregledniku
- Kopiraj poveznicu
Automatsko preuzimanje
Preuzmi zrcalo
Ponovo učitaj poveznice
@@ -461,9 +459,6 @@
Preferirani video player
Interni player
Najprije instaliraj proširenje
- VLC
- MPV
- Emitiranje na webu
Aplikacija nije pronađena
Svi jezici
Previše teksta. Nije moguće spremiti u međuspremnik.
@@ -495,7 +490,6 @@
Zadane postavke
Izgledi
Značajke
- Web preglednik
Preskoči %s
Kraj
Sažetak
@@ -646,7 +640,6 @@
Vaši CloudStream podaci su sada spremljeni u sigurnosnu kopiju. Iako je vjerojatnost mala, neki se uređaji mogu ponašati drugačije. Ako izgubite pristup aplikaciji, potpuno izbrišite podatke aplikacije i obnovite ih pomoću sigurnosne kopije. Ispričavamo se zbog mogućih neugodnosti.
Sezona %1$d epizoda %2$d izlazi
Cast mirror
- Fcast
Odaberi uređaj za emitiranje
CloudStream Wiki
Računi
@@ -686,6 +679,5 @@
Stvarno želite trajno izbrisati sve epizode u sljedećoj seriji?
\n
\n%s
- MPV YTDL
Još nije učitan nijedan titl
\ No newline at end of file
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 1426e8a38..f124ad062 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -182,7 +182,6 @@
Dokumentumfilm
Ázsiai dráma
Linkek újratöltése
- Link másolás
Letöltés mirror
Automatikus letöltés
Adatok eltárolva
@@ -227,7 +226,6 @@
Chromecast mirror
Lejátszás az alkalmazásban
Lejátszás %s
- Lejátszás böngészőben
Feliratok letöltése
Újracsatlakozás…
Húzd balra vagy jobbra a videólejátszóban az idő vezérléséhez
@@ -352,7 +350,6 @@
Mit szeretnél látni
Minden %s már letöltött
Először telepítse a bővítményt
- Webböngésző
Kinézet
Alkalmazás elrendezés
Szinkronizálás
@@ -368,7 +365,6 @@
Töltse le az összes bővítményt ebből a tárolóból?
Biztonságos mód bekapcsolva
Méret
- MPV
Alkalmazás nem található
PackageInstaller
Rendezés e szerint
@@ -403,7 +399,6 @@
Emelt
HD
HLS lejátszási lista
- VLC
Nem sikerült telepíteni az alkalmazás új verzióját
%s hitelesítve
Körvonal
@@ -528,7 +523,6 @@
Hivatkozó (opcionális)
Nem találhatóak pluginek a repóban
Repó nem található, ellenőrizze a címet vagy próbálja VPN-el
- Web Videó Cast
%s kihagyása
A kihagyási felugró ablakok mutatása nyitás/zárás esetén
Alapbeállítás
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 09dfb5322..efd0ed0cf 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -221,8 +221,6 @@
Mirror Chromecast
Putar di aplikasi
Putar di %s
- Putar di browser
- Salin tautan
Download otomatis
Download mirror
Muat ulang tautan
@@ -442,8 +440,6 @@
Bahasa
Pemutar video utama
Pemutar Bawaan
- VLC
- MPV
Terunduh %1$d %2$s
Memulai mengunduh %1$d %2$s…
Semua fitur tambahkan dimatikan karena crash, untuk memudahkanmu mencari penyebab crash.
@@ -495,9 +491,7 @@
Tidak
Memasang pembaruan…
Tidak dapat memasang versi terbaru
- Web browser
Aplikasi tidak ditemukan
- Web Video Cast
Hapus Riwayat
Tampilkan popup untuk skip sesi pembuka/akhir
Mengunduh pembaruan…
@@ -643,7 +637,6 @@
Akan datang di %s
Cermin Cast
Pilih perangkat cast
- Fcast
CloudStream Wiki
Keamanan
Akun
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index f498ccae1..590c167d9 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -243,8 +243,6 @@
Mirror Chromecast
Riproduci in app
Riproduci in %s
- Riproduci nel browser
- Copia link
Download
Mirror download
Aggiorna link
@@ -448,10 +446,6 @@
Playlist HLS
Video player preferito
Player interno
- VLC
- MPV
- Cast Web Video
- Web browser
App non trovata
Tutte le lingue
Salta %s
@@ -642,7 +636,6 @@
L\'episodio %2$d della stagione %1$d uscirà tra
Mirror cast
Seleziona dispositivo per cast
- Fcast
Wiki di CloudStream
Conti
Sicurezza
@@ -683,6 +676,5 @@
\n%s
Anteprima barra di ricerca
Abilita miniatura di anteprima sulla barra di ricerca
- MPV YTDL
Nessun sottotitolo caricato
\ No newline at end of file
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 6316d7f79..6ebe405bc 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -168,7 +168,6 @@
דרמה אסייתית
כרומקאסט את הפרק
כרומקאסט את המראה
- נגן בדפדפן
תווית כתוביות
החלף רכיבי ממשק משתמש בפוסטר
דלג על הפתיח
@@ -326,7 +325,6 @@
שגיאת הורדה, בדוק הרשאות אחסון
נגן באפליקציה
נגן ב %s
- העתק קישור
הורדה אוטומטית
טען מחדש קישורים
תווית איכות
@@ -431,7 +429,6 @@
כל %s כבר הורד
מחברים
שפה
- MPV
קרדיטים
מיין
בחר ספרייה
@@ -445,8 +442,6 @@
הורד את כל התוספים ממאגר זה?
רצועות שמע
מסלולים
- Web Video Cast
- דפדפן אינטרנט
כל התוספים נכבו עקב התרסקות כדי לעזור לך למצוא את האחד הגורם לצרות.
מוריד החבילות
עודכן %d תוספים
@@ -479,7 +474,6 @@
פלייליסט HLS
נגן וידאו מועדף
נגן פנימי
- VLC
האפליקציה לא נמצאה
כל השפות
דלג %s
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index ed9850e8e..3ca79b3d5 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -144,9 +144,6 @@
完成
進行中
デフォルト
- ウェブブラウザ
- VLC
- MPV
言語
作成者
サイズ
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 6993ec1c9..d62ecb69b 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -186,8 +186,6 @@
Chromecast 미러링
앱에서 재생
%s에서 재생
- 브라우저에서 재생
- 링크 복사
자동 다운로드
다운로드 미러
링크 새로고침
@@ -445,7 +443,6 @@
소개
유형
먼저 확장 프로그램을 설치하세요
- 웹 브라우저
앱을 찾을 수 없음
모든 언어
건너뛰기 %s
@@ -482,11 +479,8 @@
\n파일이 제거될 때까지 시작 시 확장 프로그램을 로드하지 않습니다.
HLS 재생목록
내부 플레이어
- MPV
선호하는 동영상 플레이어
- VLC
라이브러리 선택
- 웹 동영상 캐스트
이 목록이 비어 있습니다. 다른 목록으로 전환해 보세요.
필러
라이브 스트리밍 재생
@@ -579,7 +573,6 @@
자동 회전
모바일 데이터
사용 불가능
- fcast
캐스트 장치 선택
복사하는 중 오류가 발생했습니다. 로그캣을 복사하고 문의하십시오.
구독 취소
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index fe205dab7..cc68d77eb 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -163,10 +163,8 @@
Įvertinta
Praleisti šį atnaujinimą
Veiksmai
- Kopijuoti nuorodą
Paleisti programoje
Sinchronizuoti
- Paleisti naršyklėje
Pašalinti puslapį
Perkrauti nuorodos
Išjungti
@@ -192,7 +190,6 @@
Mobilūs duomenys
šaunusPrisijungimoVardas
Autoriai
- Naršyklė
Visos kalbos
4K
Pradėta siųsti %1$d %2$s…
@@ -206,7 +203,6 @@
Kalbos kodas (lt)
Baigta
Išvalyti istoriją
- VLC
Redaguoti
Wi-Fi
Greitai būs…
@@ -227,7 +223,6 @@
UHD
Dydis
Palaikoma
- MPV
ManoŠaunusPuslapis
Anonsas
Istorija
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 8f13b93c0..9469aa8e8 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -241,8 +241,6 @@
Chromecast morror
Palaist aplikācijā
Atskaņot uekšā %s
- Atskaņot internetā
- Kopēt linku
Automātiski ielādēt
Ielādēt spoguli
Pārlādēt saites
@@ -437,8 +435,6 @@
HLS atskaņošanas saraksts
Vēlamais video atskaņotājs
Iekšējais atskaņotājs
- MPV
- Web video apraide
Aplikācijs nav atrasta
Visas valodas
Beigas
@@ -513,8 +509,6 @@
Lejupielādējiet to vietņu sarakstu, kuras vēlaties izmantot
Vispirms instalējiet paplašinājumu
Atvēršana
- VLC
- Interneta mekletājs
Sākums
Izlaist %s
Noņemt no skatītajiem
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 671257492..79dc1ee7e 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -164,8 +164,6 @@
Огледало на Chromecastr
Пушти во апликацијата
Пушти на %s
- Пушти на прелистувач
- Копирај линк
Авто превземање
Превземи Mirror
Вчитај повторно врски
@@ -297,7 +295,6 @@
Износот на барањето што се користи кога плеерот е скриен
Преземи преводи
Јавна листа
- MPV
Инсталатор на пакети
ОВА
Ажурирање и резервна копија
@@ -425,7 +422,6 @@
/??
hello@world.com
+30
- VLC
Рестартирај
Цртан филм
Почна да презема %1$d %2$s…
@@ -448,7 +444,6 @@
Камера
Камера
SDR
- Веб-прелистувач
Апликацијата не е пронајдена
Корисничко име
Отвори со
@@ -488,7 +483,6 @@
Сите екстензии беа исклучени поради пад за да ви помогнат да ја пронајдете онаа што предизвикува проблеми.
Оцена: %s
Големина
- Веб-видео Cast
Сите јазици
Исчисти историја
Обележи како гледано
@@ -611,7 +605,6 @@
Сега е направена резервна копија на вашите податоци на CloudStream. Иако можноста за ова е многу мала, сите уреди можат да се однесуваат поинаку. Во ретки случаи, кога ќе се заклучите од пристап до апликацијата, целосно исчистете ги податоците на апликацијата и вратете ги од резервна копија. Многу ни е жал за какви било непријатности што произлегуваат од ова.
Ресетирај
Сезона %1$d Епизода %2$d ќе биде објавена за
- Fcast
Одбери уред да кастираш
Оневозможи оптимизација на батерија
Отклучи CloudStream
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index 1c2d855e5..bf0fede72 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -151,8 +151,6 @@
Chromecast Mirror -->
ആപ്പിൽ പ്ലേയ് ചെയ്യുക
%sയിൽ പ്ലേയ് ചെയ്യുക
- ബ്രൗസറിൽ പ്ലേയ് ചെയ്യുക
- ലിങ്ക് പകർത്തുക
ഡൌൺലോഡ് ചെയ്യൂ
മിറർ ഡൗണ്ലോഡ്
ലിങ്ക്സ് വീണ്ടും ലോഡുചെയ്യുക
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index 07154776d..e865d58db 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -2,7 +2,6 @@
Semua bahasa
Langkau %s
- Pelayar web
Sejarah
Kosongkan sejarah
Pengenalan
@@ -232,7 +231,6 @@
Dinaikkan
Lihat video dalam bahasa-bahasa ini
Normal
- Main dalam pelayar
Tambah
Diguna
Anime
@@ -323,7 +321,6 @@
Chromecast episod
Main dalam %s
Muat turun gagal, cek keizinan storan
- Salin pautan
Muat turun cermin
Muat turun sari kata
Label sub
@@ -449,8 +446,6 @@
Pengesahan Password/PIN
Sari kata belum tetapkan lagi
Disokong
- MPV
- Fcast
Ralat tidak dapat akses Clipboard, Sila cuba sekali lagi.
Ralat menyalin, Sila salin logcat dan hubungi penyokong aplikasi.
Amaran
@@ -462,8 +457,6 @@
Maks
Amaran: CloudStream 3 tidak bertanggungjawab atas penggunaan tambahan pihak ketiga dan tidak memberi sumbang kepada mereka!
Mula semula aplikasi untuk lihat perubahan.
- VLC
- MPV YTDL
Senarai ini kosong. Sila tukar yang lain.
Data mudah alih
Tambah ke kegemaran
diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml
index 31e6ef276..9d82dd47d 100644
--- a/app/src/main/res/values-my/strings.xml
+++ b/app/src/main/res/values-my/strings.xml
@@ -157,8 +157,6 @@
Chromecast ဖန်သားပြင်
အက်ပ်တွင်းဖွင့်
ဖွင့်ရန် %s
- ဘရောက်ဇာထဲမှာ ဖွင့်ရန်
- လင့်ကူးယူရန်
အလိုအလျောက်ဒေါင်းလုဒ်
လင့်များကို ပြန်စစ်ရန်
အရည်အသွေး အမှတ်အသား
@@ -338,7 +336,6 @@
နောက်သို့
အပ်ဒိတ်လုပ်ပြီး %d ဖြည့်စွက်များ
ဒေါင်းလုဒ်မလုပ်ရသေး: %d
- ဝဘ်ဘရောက်ဇာ
အက်ပ်မတွေ့ပါ
ဘာသာစကားအားလုံး
ကျော်ရန် %s
@@ -422,9 +419,6 @@
ထောက်ပံ့ထားသော
ဘာသာစကား
အဆက်များကိုအရင်သွင်းပါ
- VLC
- MPV
- ဝဘ်ထဲတွင်ဖွင့်ရန်
အစပိုင်း
အဆုံးပိုင်း
ကြည့်ရှုခဲ့သည်များကိုရှင်းရန်
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 4bef20cc2..6518eb0e7 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -235,8 +235,6 @@
Chromecast mirror
Speel in app
Speel in %s
- Speel in browser
- Kopieer link
Automatisch downloaden
Download mirror
Herlaad Linkss
@@ -409,7 +407,6 @@
Kan %s niet laden
Alle %s reeds gedownload
plugin
- VLC
Sla %s over
Links
App updates
@@ -468,10 +465,7 @@
Herhaal installatieproces
Automatisch bijwerken plugin
Sommige telefoons ondersteunen het nieuwe installatieprogramma niet. Probeer de oude optie als de updates niet worden geïnstalleerd.
- Web Video Cast
Interne speler
- MPV
- Web browser
Gemengd einde
Herhaling
Start
diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml
index 33ebe1b51..5b5577c2e 100644
--- a/app/src/main/res/values-nn/strings.xml
+++ b/app/src/main/res/values-nn/strings.xml
@@ -149,8 +149,6 @@
Kjeldefeil
Spel av i programmet
Spel av i %s
- Spel av i nettlesaren
- Kopier lenke
Automatisk nedlasting
Last inn lenker på nytt
Last ned undertekstar
diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml
index 41117f104..ac13f57f9 100644
--- a/app/src/main/res/values-no/strings.xml
+++ b/app/src/main/res/values-no/strings.xml
@@ -171,8 +171,6 @@
Støpt Speil
Spill i appen
Spill i %s
- Spill i nettleseren
- Kopier link
Automatisk nedlasting
Last ned speil
Last inn lenker på nytt
@@ -250,7 +248,6 @@
HLS-spilleliste
Foretrukket videospiller
Intern spiller
- VLC
Alle språk
Hopp over %s
Tøm historikk
@@ -283,7 +280,6 @@
Beskrivelse
Legg til konto
Normal
- MPV
Dobbelttrykk for å sette på pause
+30
Skygge
@@ -416,7 +412,6 @@
Kunne ikke logge inn på %s
Store bokstaver i undertekster
Utviklere
- Nettleser
Sensurerbart
Vev
Lenke til strøm
@@ -450,7 +445,6 @@
Fjern unødvendig informasjon fra undertekster
Ekstra
Filtrer etter foretrukket mediaspråk
- Vev-videosending
Tilbakeblikk
SD
Forfilm
diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml
index a9cff7edf..1a7d9c72f 100644
--- a/app/src/main/res/values-or/strings.xml
+++ b/app/src/main/res/values-or/strings.xml
@@ -28,14 +28,12 @@
ଟି ଅଧ୍ୟାୟ
ଅଧ୍ୟାୟ
%sରେ ଚଲାଅ
- ବ୍ରାଉଜର୍ରେ ଚଲାଅ
ଉପଶୀର୍ଷକ ଡାଉନଲୋଡ୍ କରିବା
/%d
/??
ଅଧ୍ୟାୟ %d ମୁକ୍ତିଲାଭ କଲା!
ସ୍ୱତଃ ଡାଉନଲୋଡ୍
ଲିଙ୍କ୍ଗୁଡ଼ିକୁ ପୁନଃଲୋଡ୍ କରିବା
- ଲିଙ୍କ୍ କପି କରିନେବା
ଆପ୍ରେ ଚଲାଅ
Chromecast ଅଧ୍ୟାୟ
ଅ
@@ -61,8 +59,6 @@
ପ୍ରାନ୍ତ
ଆପ୍ ମିଳିଲା ନାହିଁ
ସବୁ ଭାଷା
- VLC
- MPV
ମିଶ୍ରିତ ପ୍ରାନ୍ତ
ମିଶ୍ରିତ ଆଦ୍ୟ
ଶ୍ରେୟ
diff --git a/app/src/main/res/values-pl/array.xml b/app/src/main/res/values-pl/array.xml
index a43d7bcfe..45a8e56e1 100644
--- a/app/src/main/res/values-pl/array.xml
+++ b/app/src/main/res/values-pl/array.xml
@@ -161,32 +161,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 8f0c0b125..2f26c3f5b 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -232,8 +232,6 @@
Mirror dla Chromecast
Odtwórz w aplikacji
Odtwórz w %s
- Odtwórz w przeglądarce
- Kopiuj link
Automatyczne pobieranie
Pobierz mirror
Odśwież linki
@@ -421,10 +419,6 @@
Playlista HLS
Preferowany odtwarzacz wideo
Odtwarzacz wewnętrzny
- VLC
- MPV
- Web Video Cast
- Przeglądarka
Aplikacja nie została znaleziona
Wszystkie języki
Wyczyść historię
@@ -621,7 +615,6 @@
Resetuj
Nadchodzące w %s
Odcinek %2$d sezonu %1$d wyjdzie za
- Fcast
Wybierz urządzenie do transmisji
Mirror transmisji
Wiki CloudStream
@@ -664,6 +657,5 @@
Usuń (%1$d | %2$s)
Podgląd paska przewijania
Włącz podgląd miniatury na pasku wyszukiwania
- MPV YTDL
Nie wczytano jeszcze napisów
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 2afd50310..7fdbc6bee 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -228,8 +228,6 @@
Alternativa pelo Chromecast
Reproduzir na app
Reproduzir no %s
- Reproduzir no navegador
- Copiar link
Transferência Automática
Transferir por servidor alternativo
Recarregar links
@@ -441,18 +439,14 @@
Abertura
Selecionar Biblioteca
Contorna o bloqueio de URLs raw do GitHub usando jsDelivr. Pode atrasar as atualizações por uns dias.
- VLC
Todas as linguagens
Atualizado (Novo para Antigo)
Inscrito
HDR
Reiniciar
- Navegador Web
Atualizado (Antigo para Novo)
- Web Video Cast
DVD
Instalador de pacotes
- MPV
Remover dos assistidos
Não foi possível instalar a nova versão do aplicativo
Inscrição cancelada em %s
@@ -616,8 +610,7 @@
Desativar a otimização da bateria
Para garantir descarregamentos ininterruptos e notificações de programas de TV subscritos, o CloudStream precisa de permissão para ser executado em segundo plano. Ao premir OK, será direcionado para informações da aplicação. Aí, desloque-se para utilização da bateria da aplicação e defina a utilização da bateria para sem restrições. Tenha em atenção que esta permissão não significa que o CS3 irá esgotar a sua bateria. Este só funcionará em segundo plano quando necessário, como ao receber notificações ou baixar vídeos de extensões oficiais. Se optar por cancelar, pode ajustar esta definição mais tarde em definições gerais.
Reiniciar
- Episódio %2$d da Temporada %1$d vai ser lançado em
- Fcast
+ Episódio %1$d Episódio %2$d vai ser lançado em
Escolha o dispositivo
Transmitir
hide_player_control_names_key
diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml
index 378e3aaec..8f0e14cbc 100644
--- a/app/src/main/res/values-qt/strings.xml
+++ b/app/src/main/res/values-qt/strings.xml
@@ -144,8 +144,6 @@
aoohaaahhu ahouuhhh
ooo-ahahaauuh aaahhu
ooo-ahah ohaauuh %s
- ahoha ooo-ahahohoohah oooohh
- aauugghhahhaauugghh
aaaghhoooohh aaahhu ahooo
ohooo-ahahaohaohahhhoouuh
ahoooaaahhuahaaahhuoha
@@ -318,7 +316,6 @@
aaahh uuuugggh
oooohh oooogggoog
uuuuhhhaagg
- uuh
uuh aahh uugg oooogg ag aagg ug ooooggguh
ooooggg %d
aahh
@@ -472,8 +469,6 @@
ooh oooohhhooh oogg oooogg ooh uuh uh g ooogg oh uuhh uug uuhh ooh aah uuuuggg ooooggg
ooooggg
aaaagggh uuuugg
- ooh uuuhh aahh
- uuh uuuuhhh
aah ooh uuugg
uuuuhhh
uuuuhh aagg oooohhh
@@ -617,8 +612,6 @@
%s (Disabled)
Rating: %s
aaaagg
- oog
- uuuhh
aaaagg aahh oooohh
uuuuuk
aaagg aaaahhh
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 8105aa3e8..ec1115112 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -228,8 +228,6 @@
Chromecast alternativ
Redă în Aplicație
Redă în %s
- Redă în Browser
- Copiază link-ul
Auto-descărcare
Descărcă prin Alernativă
Reîncărcare link-uri
@@ -467,7 +465,6 @@
Nu
Abonat la %s
Aplicația va fi actualizată la ieșire
- Web Video Cast
Ocoliri ISP
Anterior
Sortează
@@ -475,7 +472,6 @@
Filtrați în funcție de limba media preferată
Episodul %d a fost lansat!
Android TV
- VLC
Urmăriți videoclipuri în aceste limbi
Revenire
Acțiuni
@@ -483,7 +479,6 @@
URL invalid
Toate extensiile au fost dezactivate din cauza unei defecțiuni pentru a vă ajuta să o găsiți pe cea care cauzează probleme.
Se descarcă actualizarea aplicației…
- Browser web
CloudStream nu are niciun site instalat din start. Trebuie să instalați site-urile din depozite.
\n
\nAlăturați-vă Discord-ului nostru sau căutați online.
@@ -522,7 +517,6 @@
Se actualizează emisiunile abonate
Abonat
Lista publică
- MPV
Moştenit
Test de furnizor
Furnizori
@@ -640,6 +634,5 @@
Sezonul %1$d Episod %2$d va fi lansat în
Selectați divece-ul pe care doriți să faceți cast
Cast mirror
- Fcast
hide_player_control_names_key
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e2f6a4b13..c251ca9bb 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -190,7 +190,6 @@
Торренты
Другое
Ошибка загрузки, проверьте разрешения хранилища
- Копировать ссылку
Автоскачивание
Загрузка. Зеркало
Сезон
@@ -270,8 +269,6 @@
Размер
Авторы
Поддерживается
- VLC
- MPV
Пропустить %s
Концовка
Используйте яркость системы в проигрывателе приложения вместо темного наложения
@@ -293,7 +290,6 @@
Неожиданная ошибка плеера
Эпизод Chromecast
Воспроизведение на %s
- Воспроизвести в браузере
Скачать субтитры
Знак качества
Переключение элементов интерфейса на плакате
@@ -309,7 +305,6 @@
%d из 10
Посмотреть информацию о сбое
Предпочитаемый видеоплеер
- Веб-браузер
Приложение не найдено
Все языки
Вступление
@@ -492,7 +487,6 @@
Отображать рандомную кнопку в библиотеке и главной странице
Рандомная кнопка
Legacy (старый)
- Web Video Cast
Не отправляет данные
Перезагрузить ссылки
Предпочтительные медиа
@@ -620,7 +614,6 @@
Сброс
Сезон %1$d Эпизод %2$d выйдет
Выйдет %s
- Fcast
Выберите девайс для трансляции
hide_player_control_names_key
В данный момент загрузок нет.
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 4e38be6b9..196ed1d60 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -188,7 +188,6 @@
Žiadne titulky
Anime
Kreslené
- Skopírovať odkaz
Automaticky stiahnuť
Zrkadlo sťahovania
Zamknúť
@@ -320,7 +319,6 @@
Ázijské drámy
Anime
Chyba zdroja
- Prehrať v prehliadači
Štítok dabingu
Štítok titulkov
Názov
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index e4ee98f96..8b1e4cc15 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -119,7 +119,6 @@
Asalkiisa
Isla appkan ku daawo
Ku daawo %s
- Ku daawo barawsarka
Dejinta iskeed ah
Deji toorentiga(mirror)
Cusbooneysii lifaaqyada
@@ -239,7 +238,6 @@
Eeg xogaha hoose 🐈
Badhinka xajmiga daaraha
Fashil ka yimi dhiibaha
- Koobu garee lifaaqa
Sharraxaad ma leh
Sharraxaadda
Duluc ma leh
@@ -472,10 +470,6 @@
Kordhiyeyaasha
Liiska bulshada kale
Muqaal daaraha appka
- VLC
- MPV
- Web Video Cast
- Barawsarka
Kuuguma jiro appkaasi
Dhammaan luuqadaha
Is dhaafi %s
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 9757fdf67..06f388848 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -148,8 +148,6 @@
Chromecasta en Länk
Spela upp i appen
Spela upp i %s
- Spela upp i webbläsaren
- Kopiera länk
Automatisk nerladdning
Ladda ner en specifik länk
Ladda om alla länkar
@@ -379,13 +377,11 @@
Visa sub
Kunde inte öppna appen
GitHub Proxy
- Webbläsarens videospelare
Installerar uppdatering till appen…
Kunde inte nå GitHub, sätter på jsDelivr proxy…
Leverantörer
Nytt webbplatsnamn
Ta bort reklam från undertexter
- VLC
Alla språk
Rensa historik
PackageInstaller
@@ -409,8 +405,6 @@
Videospelare
Öppna med
Synkronisera undertexter
- MPV
- Web Video Cast
Misslyckades
Ja
Videospår
@@ -619,7 +613,6 @@
Kommer ut om %s
Fel vid kopiering, kopiera logcat och kontakta appsupport.
Media
- Fcast
Cast mirror
Säsong %1$d Avsnitt %2$d kommer att släppas om
Välj cast-enhet
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index c8c3243cd..a2b4cadd2 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -266,8 +266,6 @@
மறுதொடக்கம்
விவரம்
ஆசிரியர்கள்
- வலை வீடியோ நடிகர்கள்
- இணைய உலாவி
%s ஐத் தவிர்க்கவும்
மறுபரிசீலனை செய்யுங்கள்
அறிமுகம்
@@ -365,7 +363,6 @@
முன்மாதிரி தளவமைப்பு
பதிவிறக்கம் செய்யப்பட்ட கோப்பு
பகுத்தல்
- எம்.பி.வி.
உங்கள் நூலகம் காலியாக உள்ளது :(
\n நூலகக் கணக்கில் உள்நுழைக அல்லது உங்கள் உள்ளக நூலகத்தில் காட்சிகளைச் சேர்க்கவும்.
குழுவிலகவும்
@@ -431,8 +428,6 @@
மூல பிழை
தொலை பிழை
ரெண்டரர் பிழை
- உலாவியில் விளையாடுங்கள்
- இணைப்பை நகலெடுக்கவும்
ஆட்டோ பதிவிறக்கம்
கண்ணாடியைப் பதிவிறக்கவும்
இணைப்புகளை மீண்டும் ஏற்றவும்
@@ -520,7 +515,6 @@
அளவு
ஆதரிக்கப்பட்டது
முதலில் நீட்டிப்பை நிறுவவும்
- Fcast
காச்ட் சாதனத்தைத் தேர்ந்தெடுக்கவும்
அனைத்து மொழிகளும்
ஆம்
@@ -551,7 +545,6 @@
பதிவிறக்கம் செய்யப்படவில்லை: %d
புதுப்பிக்கப்பட்டது %d செருகுநிரல்கள்
உள் வீரர்
- வி.எல்.சி.
திறப்பு
கலப்பு திறப்பு
வரவு
diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml
index d832144dd..5fd25031e 100644
--- a/app/src/main/res/values-tl/strings.xml
+++ b/app/src/main/res/values-tl/strings.xml
@@ -174,8 +174,6 @@
Chromecast Mirror
I-play sa App
I-play sa %s
- I-play sa browser
- Kopyahin ang Link
Awtomatiking i-download
Download mirror
Subukan muli
diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml
index 22a94ebf0..4ec45e6d1 100644
--- a/app/src/main/res/values-tr/array.xml
+++ b/app/src/main/res/values-tr/array.xml
@@ -33,22 +33,6 @@
- 6
-
- - @string/player_settings_play_in_app
- - @string/player_settings_play_in_vlc
- - @string/player_settings_play_in_mpv
- - @string/player_settings_play_in_web
- - @string/player_settings_play_in_browser
-
-
-
- - 1
- - 2
- - 5
- - 4
- - 3
-
-
- @string/resolution_and_title
- @string/title
@@ -187,32 +171,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index d40e5d7aa..b516de738 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -258,8 +258,6 @@
Bağlantıyı Chromecast ile yayınla
Burada oynat
%s üzerinden oynat
- Tarayıcıda oynat
- Bağlantıyı kopyala
Otomatik indir
Şu kaynaktan indir
Bağlantıları yenile
@@ -482,10 +480,6 @@
HLS Oynatma Listesi
Tercih edilen video oynatıcısı
Dahili oynatıcı
- VLC
- MPV
- Web Video Yayını
- İnternet tarayıcısı
Uygulama bulunamadı
Geçmiş
İzlendi olarak işaretle
@@ -669,7 +663,6 @@
Sezon %1$d Bölüm %2$d tarihinde yayınlanacak
Yansıtılacak cihaz seç
Ekran yansıtma
- Fcast
CloudStream Viki
Güvenlik
Hesaplar
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 010af23c9..c5e234f0d 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -248,7 +248,6 @@
Змінити розмір
Короткий зміст
Фільми
- Скопіювати посилання
Перезавантажити посилання
Документальні фільми
NSFW
@@ -257,7 +256,6 @@
Торент
Мітка якості
NSFW
- Переглянути в браузері
Несподівана помилка плеєра
Помилка завантаження, перевірте дозволи на зберігання
Епізод Chromecast
@@ -445,10 +443,6 @@
Спочатку встановіть розширення
Список відтворення HLS
Вбудований плеєр
- VLC
- MPV
- Web Video Cast
- Веббраузер
Ендінґ
Коротке повторення
Пропустити %s
@@ -620,7 +614,6 @@
Скинути
Наступний через %s
%1$d сезон %2$d епізод вийде через
- Fcast
Оберіть пристрій для трансляції
Трансляція через дзеркало
CloudStream Wiki
@@ -663,6 +656,5 @@
\n%s
Попередній перегляд повзунка
Ввімкнути мініатюру попереднього перегляду на повзунку
- MPV YTDL
Субтитри ще не завантажено
\ No newline at end of file
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 5a32dfe8a..06508bc9e 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -243,7 +243,6 @@
Chromecast mirror
ایپ میں چلائیں
%s میں چلائیں
- کاپی لنک
ڈاؤنلوڈ mirror
لنکس کو دوبارہ لوڈ کریں
سب ٹائٹلز ڈاؤن لوڈ
@@ -336,7 +335,6 @@
کوئی زیرنویس میں دیری نہیں
این ایس ایف ڈبلیو
آٹو ڈاؤن لوڈ
- browser میں چلائیں
بہت زیادہ سیٹ ہونے پر کم میموری والی ڈیوائس(جیسے کہ Android TV) پر کریشوں کا سبب بنتا ہے.
Remove site
Add a clone of an existing site, with a different URL
@@ -361,7 +359,6 @@
قرارداد اور عنوان
HDR
%s سے ان سبسکرائب کیا گیا
- ویب ویڈیو کاسٹ
18+
لاگ
https://example.com/example.mp4
@@ -427,11 +424,8 @@
حالت
سائز
زبان
- وی ایل سی
ترجیحی ویڈیو پلیئر
اندرونی پلیئر
- ایم پی وی
- ویب براؤزر
ایپ نہیں ملی
Recap
مخلوط اختتام
diff --git a/app/src/main/res/values-vi/array.xml b/app/src/main/res/values-vi/array.xml
index f363befda..c6ff1c4e2 100644
--- a/app/src/main/res/values-vi/array.xml
+++ b/app/src/main/res/values-vi/array.xml
@@ -153,32 +153,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 53b19bd8b..5c4cd380e 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -243,8 +243,6 @@
Chiếu Chromecast
Xem với trình phát mặc định
Xem với trình phát %s
- Xem tại trình duyệt
- Sao chép liên kết
Tự động tải xuống
Nguồn tải xuống
Lấy link mới nhất
@@ -472,10 +470,6 @@
Hỗ trợ
Ngôn ngữ
Cài đặt tiện ích trước
- VLC
- MPV
- Web Video Cast
- Trình duyệt web
Không thấy ứng dụng
Tất cả ngôn ngữ
Tua %s
@@ -671,11 +665,9 @@
\n
\n%s
Xóa plugin
- Fcast
Ngày phát hành (Cũ đến mới)
Ẩn tên các nút điều khiển
Bật chế độ xem trước hình thu nhỏ trên seekbar
Xem trước Seekbar
- MPV YTDL
Chưa tải phụ đề
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index b8b8b4884..3c002c29a 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -258,8 +258,6 @@
Chromecast 鏡像
在應用程式中播放
在 %s 中播放
- 在瀏覽器中播放
- 複製連結
自動下載
下載鏡像
重新載入連結
@@ -482,10 +480,6 @@
HLS 播放清單
偏好影片播放器
內部播放器
- VLC
- MPV
- 網路影片播放
- 網頁瀏覽器
未找到應用程式
所有語言
跳過 %s
@@ -663,7 +657,6 @@
使用指紋、面容 ID、PIN、圖案和密碼解除鎖定應用程式。
由於多次嘗試失敗,此畫面已關閉。請重新啟動應用程式。
剪貼簿存取失敗,請再試一次。
- Fcast
複製失敗,請複製 logcat 內容並聯繫應用程式支援者。
無法開啟 CloudStream 的應用程式資訊頁面。
您的 CloudStream 資料已完成備份。儘管可能性非常低,但因不同裝置的行為都有所不同,在極少數情況下,您可能會無法存取本應用程式。此時請完全清除本應用程式的資料,再使用已有的備份進行還原。若因此造成任何不便,我們深感抱歉。
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index ff85df32e..45e350439 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -259,8 +259,6 @@
Chromecast 镜像
在应用中播放
在 %s 中播放
- 在浏览器中播放
- 复制链接
自动下载
下载镜像
重新加载链接
@@ -483,10 +481,6 @@
HLS 播放列表
首选视频播放器
内部播放器
- VLC
- MPV
- 投屏
- 浏览器
未找到应用
所有语言
跳过 %s
@@ -665,7 +659,6 @@
在多次尝试失败后,提示将关闭。只需重启应用程序再试。
即将在 %s
CloudStream Wiki
- Fcast
选择投射设备
%1$d季%2$d集将在
投射镜像
@@ -674,7 +667,6 @@
打开本地视频
安全
访问智能手机或电脑上的 %s 并输入上述代码
- MPV YTDL
进度条预览
启用进度条预览缩略图
删除插件
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index b077997fe..a987420e9 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -50,24 +50,6 @@
- @string/nsfw
-
- - @string/player_settings_play_in_app
- - @string/player_settings_play_in_vlc
- - @string/player_settings_play_in_mpv
- - @string/player_settings_play_in_mpvytdl
- - @string/player_settings_play_in_web
- - @string/player_settings_play_in_browser
-
-
-
- - 1
- - 2
- - 5
- - 6
- - 4
- - 3
-
-
- @string/resolution_and_title
- @string/title
@@ -226,32 +208,6 @@
- @string/show_title_key
-
- - @string/episode_action_chromecast_episode
- - @string/episode_action_chromecast_mirror
- - @string/episode_action_play_in_app
- - @string/episode_action_play_in_format
- - @string/episode_action_play_in_browser
- - @string/episode_action_copy_link
- - @string/episode_action_auto_download
- - @string/episode_action_download_mirror
- - @string/episode_action_download_subtitle
- - @string/episode_action_reload_links
-
-
-
- - 4
- - 5
- - 1
- - 2
- - 3
- - 9
- - 6
- - 7
- - 13
- - 8
-
-
- @string/automatic
- @string/phone_layout
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9d9362bec..56f2465d2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -17,7 +17,7 @@
subtitle_settings_chromecast_key
quality_pref_key
quality_pref_mobile_data_key
- player_pref_key
+ player_default_key
prefer_limit_title_key
prefer_limit_title_rez_key
apk_installer_key
@@ -381,8 +381,6 @@
Cast mirror
Play in app
Play in %s
- Play in browser
- Copy link
Auto download
Download mirror
Reload links
@@ -662,12 +660,6 @@
HLS Playlist
Preferred video player
Internal player
- VLC
- MPV
- MPV YTDL
- Web Video Cast
- Fcast
- Web browser
Select cast device
App not found
All Languages
diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml
index 73f9bb5bf..a2575e315 100644
--- a/app/src/main/res/xml/settings_player.xml
+++ b/app/src/main/res/xml/settings_player.xml
@@ -22,7 +22,7 @@
From cffc14a6df0e8ff1d8dbf46d745586a578158d72 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Tue, 24 Sep 2024 15:28:10 +0200
Subject: [PATCH 006/956] fixes critical issues with #1329
---
.../lagradost/cloudstream3/CommonActivity.kt | 2 +-
.../cloudstream3/actions/OpenInAppAction.kt | 101 +++++++++---------
.../cloudstream3/actions/VideoClickAction.kt | 42 +++++++-
.../actions/temp/CopyClipboardAction.kt | 2 +-
.../cloudstream3/actions/temp/MpvKtPackage.kt | 2 +-
.../cloudstream3/actions/temp/MpvPackage.kt | 2 +-
.../actions/temp/PlayInBrowserAction.kt | 13 +--
.../actions/temp/ViewM3U8Action.kt | 2 +-
.../cloudstream3/actions/temp/VlcPackage.kt | 16 +--
.../actions/temp/WebVideoCastPackage.kt | 2 +-
.../actions/temp/fcast/FcastAction.kt | 2 +-
.../ui/result/ResultViewModel2.kt | 4 +-
12 files changed, 113 insertions(+), 77 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index 50e6d8c98..8498925c8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -222,7 +222,7 @@ object CommonActivity {
val actionUid = getKey("last_click_action") ?: return@registerForActivityResult
Log.d(TAG, "Loading action $actionUid result handler")
val action = VideoClickActionHolder.getByUniqueId(actionUid) as? OpenInAppAction ?: return@registerForActivityResult
- action.onResult(act, result.data)
+ action.onResultSafe(act, result.data)
removeKey("last_click_action")
removeKey("last_opened_id")
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 99c1ac38b..4952bde6b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -1,16 +1,13 @@
package com.lagradost.cloudstream3.actions
import android.app.Activity
-import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
-import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.net.toUri
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
-import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.activityResultLauncher
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
@@ -21,9 +18,6 @@ import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
import com.lagradost.cloudstream3.utils.DataStoreHelper
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
import java.io.File
fun updateDurationAndPosition(position: Long, duration: Long) {
@@ -39,7 +33,13 @@ fun updateDurationAndPosition(position: Long, duration: Long) {
fun makeTempM3U8Intent(
context: Context,
intent: Intent,
- result: LinkLoadingResult) {
+ result: LinkLoadingResult
+) {
+ if (result.links.size == 1) {
+ intent.setDataAndType(result.links.first().url.toUri(), "video/*")
+ return
+ }
+
intent.apply {
addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION)
@@ -47,37 +47,29 @@ fun makeTempM3U8Intent(
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
}
- val outputDir = context.cacheDir
+ val outputFile = File.createTempFile("mirrorlist", ".m3u8", context.cacheDir)
+ var text = "#EXTM3U\n#EXT-X-VERSION:3"
- if (result.links.size == 1) {
- intent.setDataAndType(result.links.first().url.toUri(), "video/*")
- } else {
- val outputFile = File.createTempFile("mirrorlist", ".m3u8", outputDir)
-
- var text = "#EXTM3U\n#EXT-X-VERSION:3"
-
- result.links.forEachIndexed { index, link ->
- text += "\n#EXTINF:$index,${link.name}\n${link.url}"
- }
-
- //With subtitles it doesn't work for no reason :(
- /*for (sub in result.subs) {
- val normalizedName = sub.name.replace("[^a-zA-Z0-9 ]".toRegex(), "")
- text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.languageCode}\",URI=\"${sub.url}\""
- }*/
-
- text += "\n#EXT-X-ENDLIST"
-
- outputFile.writeText(text)
-
- intent.setDataAndType(
- FileProvider.getUriForFile(
- context,
- context.applicationContext.packageName + ".provider",
- outputFile
- ), "application/x-mpegURL"
- )
+ result.links.forEach { link ->
+ text += "\n#EXTINF:0,${link.name}\n${link.url}"
}
+
+ //With subtitles it doesn't work for no reason :(
+ /*for (sub in result.subs) {
+ val normalizedName = sub.name.replace("[^a-zA-Z0-9 ]".toRegex(), "")
+ text += "\n#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID=\"subs\",NAME=\"${normalizedName}\",DEFAULT=NO,AUTOSELECT=NO,FORCED=NO,LANGUAGE=\"${sub.languageCode}\",URI=\"${sub.url}\""
+ }*/
+
+ text += "\n#EXT-X-ENDLIST"
+ outputFile.writeText(text)
+
+ intent.setDataAndType(
+ FileProvider.getUriForFile(
+ context,
+ context.applicationContext.packageName + ".provider",
+ outputFile
+ ), "application/x-mpegURL"
+ )
}
abstract class OpenInAppAction(
@@ -85,15 +77,16 @@ abstract class OpenInAppAction(
open val packageName: String,
private val intentClass: String? = null,
private val action: String = Intent.ACTION_VIEW
-): VideoClickAction() {
+) : VideoClickAction() {
override val name: UiText
get() = txt(R.string.episode_action_play_in_format, appName)
override val isPlayer = true
- override fun shouldShow(context: Context?, video: ResultEpisode?) = context?.isAppInstalled(packageName) == true
+ override fun shouldShow(context: Context?, video: ResultEpisode?) =
+ context?.isAppInstalled(packageName) != false
- override fun runAction(
+ override suspend fun runAction(
context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
@@ -107,28 +100,36 @@ abstract class OpenInAppAction(
}
putExtra(context, intent, video, result, index)
setKey("last_opened_id", video.id)
- try {
- CoroutineScope(Dispatchers.IO).launch {
- activityResultLauncher?.launch(intent)
- }
- } catch (_: ActivityNotFoundException) {
- showToast(R.string.app_not_found_error, Toast.LENGTH_LONG)
- } catch (t: Throwable) {
- logError(t)
- showToast(t.toString(), Toast.LENGTH_LONG)
- }
+ activityResultLauncher?.launch(intent)
}
/**
* Before intent is sent, this function is called to put extra data into the intent.
* @see VideoClickAction.runAction
* */
- abstract fun putExtra(context: Context, intent: Intent, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+ @Throws
+ abstract suspend fun putExtra(
+ context: Context,
+ intent: Intent,
+ video: ResultEpisode,
+ result: LinkLoadingResult,
+ index: Int?
+ )
/**
* This function is called when the app is opened again after the intent was sent.
* You can use it to for example update duration and position.
* @see updateDurationAndPosition
*/
+ @Throws
abstract fun onResult(activity: Activity, intent: Intent?)
+
+ /** Safe version of onResult, we don't trust extension devs to not crash the app */
+ fun onResultSafe(activity: Activity, intent: Intent?) {
+ try {
+ onResult(activity, intent)
+ } catch (t: Throwable) {
+ logError(t)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index f66ed74d9..eada9fea1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -1,8 +1,13 @@
package com.lagradost.cloudstream3.actions
import android.app.Activity
+import android.content.ActivityNotFoundException
import android.content.Context
+import android.widget.Toast
import com.lagradost.api.Log
+import com.lagradost.cloudstream3.CommonActivity
+import com.lagradost.cloudstream3.ErrorLoadingException
+import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.temp.CopyClipboardAction
import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
import com.lagradost.cloudstream3.actions.temp.MpvKtPreviewPackage
@@ -13,9 +18,11 @@ import com.lagradost.cloudstream3.actions.temp.ViewM3U8Action
import com.lagradost.cloudstream3.actions.temp.VlcPackage
import com.lagradost.cloudstream3.actions.temp.WebVideoCastPackage
import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
+import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLinkType
import kotlin.reflect.jvm.jvmName
@@ -38,7 +45,7 @@ object VideoClickActionHolder {
fun makeOptionMap(activity: Activity?, video: ResultEpisode) = allVideoClickActions
// We need to have index before filtering
.mapIndexed { id, it -> it to id + ACTION_ID_OFFSET }
- .filter { it.first.shouldShow(activity, video) }
+ .filter { it.first.shouldShowSafe(activity, video) }
.map { it.first.name to it.second }
@@ -54,7 +61,7 @@ object VideoClickActionHolder {
?.second
}
- fun getPlayers(activity: Activity? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShow(activity, null) }
+ fun getPlayers(activity: Activity? = null) = allVideoClickActions.filter { it.isPlayer && it.shouldShowSafe(activity, null) }
}
abstract class VideoClickAction {
@@ -74,8 +81,20 @@ abstract class VideoClickAction {
fun uniqueId() = "$sourcePlugin:${this::class.jvmName}"
+ @Throws
abstract fun shouldShow(context: Context?, video: ResultEpisode?): Boolean
+ /** Safe version of shouldShow, as we don't trust extension devs to handle exceptions,
+ * however no dev *should* throw in shouldShow */
+ fun shouldShowSafe(context: Context?, video: ResultEpisode?): Boolean {
+ return try {
+ shouldShow(context,video)
+ } catch (t : Throwable) {
+ logError(t)
+ false
+ }
+ }
+
/**
* This function is called when the action is clicked.
* @param context The current activity
@@ -83,5 +102,22 @@ abstract class VideoClickAction {
* @param result The result of the link loading, contains video & subtitle links
* @param index if oneSource is true, this is the index of the selected source
*/
- abstract fun runAction(context: Context?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+ @Throws
+ abstract suspend fun runAction(context: Context?, video: ResultEpisode, result: LinkLoadingResult, index: Int?)
+
+ /** Safe version of runAction, as we don't trust extension devs to handle exceptions */
+ fun runActionSafe(context: Context?, video: ResultEpisode, result: LinkLoadingResult, index: Int?) = ioSafe {
+ try {
+ runAction(context, video, result, index)
+ } catch (_ : NotImplementedError) {
+ CommonActivity.showToast("runAction has not been implemented for ${name.asStringNull(context)}, please contact the extension developer of $sourcePlugin", Toast.LENGTH_LONG)
+ } catch (error : ErrorLoadingException) {
+ CommonActivity.showToast(error.message, Toast.LENGTH_LONG)
+ } catch (_: ActivityNotFoundException) {
+ CommonActivity.showToast(R.string.app_not_found_error, Toast.LENGTH_LONG)
+ } catch (t : Throwable) {
+ logError(t)
+ CommonActivity.showToast(t.toString(), Toast.LENGTH_LONG)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
index e054b5ce2..481917e5e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
@@ -14,7 +14,7 @@ class CopyClipboardAction: VideoClickAction() {
override fun shouldShow(context: Context?, video: ResultEpisode?) = true
- override fun runAction(
+ override suspend fun runAction(
context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
index f5ded49b8..6514de174 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
@@ -34,7 +34,7 @@ open class MpvKtPackage(
ExtractorLinkType.M3U8
)
- override fun putExtra(
+ override suspend fun putExtra(
context: Context,
intent: Intent,
video: ResultEpisode,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
index 4c66d0450..fa45aed8f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -31,7 +31,7 @@ open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv
"is.xyz.mpv.MPVActivity"
) {
- override fun putExtra(
+ override suspend fun putExtra(
context: Context,
intent: Intent,
video: ResultEpisode,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
index de32bb4b3..1e11281a5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -5,7 +5,6 @@ import android.content.Intent
import android.net.Uri
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.VideoClickAction
-import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.txt
@@ -26,19 +25,15 @@ class PlayInBrowserAction: VideoClickAction() {
override fun shouldShow(context: Context?, video: ResultEpisode?) = true
- override fun runAction(
+ override suspend fun runAction(
context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
val link = result.links.getOrNull(index ?: 0) ?: return
- try {
- val i = Intent(Intent.ACTION_VIEW)
- i.data = Uri.parse(link.url)
- context?.startActivity(i)
- } catch (e: Exception) {
- logError(e)
- }
+ val i = Intent(Intent.ACTION_VIEW)
+ i.data = Uri.parse(link.url)
+ context?.startActivity(i)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
index c14168e96..fa7fc1173 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
@@ -16,7 +16,7 @@ class ViewM3U8Action: VideoClickAction() {
override fun shouldShow(context: Context?, video: ResultEpisode?) = true
- override fun runAction(
+ override suspend fun runAction(
context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
index ecb37fdc6..18143c001 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Build
+import androidx.core.net.toUri
import com.lagradost.api.Log
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.actions.OpenInAppAction
@@ -32,18 +33,21 @@ class VlcPackage: OpenInAppAction(
Intent.ACTION_VIEW
}
) {
- override val oneSource = false
+ // while VLC supports multi links, it has poor support, so we disable it for now
+ override val oneSource = true
- override fun putExtra(
+ override suspend fun putExtra(
context: Context,
intent: Intent,
video: ResultEpisode,
result: LinkLoadingResult,
index: Int?
) {
-
- makeTempM3U8Intent(context, intent, result)
-
+ if (index != null) {
+ intent.setDataAndType(result.links[index].url.toUri(), "video/*")
+ } else {
+ makeTempM3U8Intent(context, intent, result)
+ }
val position = getViewPos(video.id)?.position ?: 0L
intent.putExtra("from_start", false)
@@ -51,7 +55,7 @@ class VlcPackage: OpenInAppAction(
intent.putExtra("secure_uri", true)
intent.putExtra("title", video.name)
- val subsLang = getKey(SUBTITLE_AUTO_SELECT_KEY) ?: "en"
+ val subsLang = getKey(SUBTITLE_AUTO_SELECT_KEY) ?: "en"
result.subs.firstOrNull {
subsLang == it.languageCode
}?.let {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
index f8419f63c..4f44b3969 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
@@ -28,7 +28,7 @@ class WebVideoCastPackage: OpenInAppAction(
ExtractorLinkType.M3U8
)
- override fun putExtra(
+ override suspend fun putExtra(
context: Context,
intent: Intent,
video: ResultEpisode,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
index c0f92e4df..9de8e7d5d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -26,7 +26,7 @@ class FcastAction: VideoClickAction() {
override fun shouldShow(context: Context?, video: ResultEpisode?) = FcastManager.currentDevices.isNotEmpty()
- override fun runAction(
+ override suspend fun runAction(
context: Context?,
video: ResultEpisode,
result: LinkLoadingResult,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index b5f83201e..bf3b82a91 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -1625,7 +1625,7 @@ class ResultViewModel2 : ViewModel() {
action.sourceTypes,
action.name
) { (result, index) ->
- action.runAction(
+ action.runActionSafe(
activity,
click.data,
result,
@@ -1634,7 +1634,7 @@ class ResultViewModel2 : ViewModel() {
}
} else {
loadLinks(click.data, isVisible = true, action.sourceTypes) { links ->
- action.runAction(
+ action.runActionSafe(
activity,
click.data,
links,
From d899ecb82f67970bb32b4f2cd7a5cd0ae2c0248e Mon Sep 17 00:00:00 2001
From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com>
Date: Tue, 24 Sep 2024 14:23:33 +0000
Subject: [PATCH 007/956] Fix navigation bar in light mode (#1350)
---
app/src/main/res/color/item_select_color.xml | 4 ++--
app/src/main/res/values/colors.xml | 3 +--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/app/src/main/res/color/item_select_color.xml b/app/src/main/res/color/item_select_color.xml
index 5a9453b7f..208afb18b 100644
--- a/app/src/main/res/color/item_select_color.xml
+++ b/app/src/main/res/color/item_select_color.xml
@@ -3,5 +3,5 @@
-
-
\ No newline at end of file
+
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 5154b65ff..f63308b61 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -89,5 +89,4 @@
#48E484
#ea596e
#FF9800
- #F5F5F5
-
\ No newline at end of file
+
From 078de970d522f7ead336c7d93d3455867b78d5e0 Mon Sep 17 00:00:00 2001
From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com>
Date: Thu, 26 Sep 2024 17:41:54 +0000
Subject: [PATCH 008/956] Update FcastAction.kt (#1352)
---
.../lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
index 9de8e7d5d..f69da0e05 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -53,7 +53,7 @@ class FcastAction: VideoClickAction() {
session.sendMessage(
Opcode.Play,
PlayMessage(
- "video/*",
+ link.type.getMimeType(),
link.url,
time = position?.let { it / 1000.0 },
headers = mapOf(
@@ -64,4 +64,4 @@ class FcastAction: VideoClickAction() {
)
}
}
-}
\ No newline at end of file
+}
From bd95ecf5ba0d30da76103597b40ebe77b80d2ad9 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 26 Sep 2024 20:32:22 +0200
Subject: [PATCH 009/956] Small changes to VideoClickAction API
---
.../cloudstream3/actions/OpenInAppAction.kt | 2 +-
.../cloudstream3/actions/VideoClickAction.kt | 74 +++++++++++++++++--
.../cloudstream3/actions/temp/MpvPackage.kt | 9 ++-
.../actions/temp/PlayInBrowserAction.kt | 2 +-
.../actions/temp/ViewM3U8Action.kt | 2 +-
.../cloudstream3/utils/AppContextUtils.kt | 9 ++-
6 files changed, 84 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 4952bde6b..8abc5e665 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -100,7 +100,7 @@ abstract class OpenInAppAction(
}
putExtra(context, intent, video, result, index)
setKey("last_opened_id", video.id)
- activityResultLauncher?.launch(intent)
+ launchResult(intent)
}
/**
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index eada9fea1..0d1e87a74 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -3,10 +3,14 @@ package com.lagradost.cloudstream3.actions
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
+import android.content.Intent
+import android.os.Bundle
import android.widget.Toast
+import androidx.core.app.ActivityOptionsCompat
import com.lagradost.api.Log
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.ErrorLoadingException
+import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.temp.CopyClipboardAction
import com.lagradost.cloudstream3.actions.temp.MpvKtPackage
@@ -25,15 +29,29 @@ import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLinkType
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+import java.util.concurrent.Callable
+import java.util.concurrent.FutureTask
import kotlin.reflect.jvm.jvmName
object VideoClickActionHolder {
- val allVideoClickActions = threadSafeListOf(
- PlayInBrowserAction(), CopyClipboardAction(),
- VlcPackage(), ViewM3U8Action(),
- MpvPackage(), MpvYTDLPackage(),
- WebVideoCastPackage(), MpvKtPackage(), MpvKtPreviewPackage(),
- FcastAction()
+ val allVideoClickActions = threadSafeListOf(
+ // Default
+ PlayInBrowserAction(),
+ CopyClipboardAction(),
+ ViewM3U8Action(),
+ // main support external apps
+ VlcPackage(),
+ MpvPackage(),
+ FcastAction(),
+ // forks/backup apps
+ WebVideoCastPackage(),
+ MpvYTDLPackage(),
+ MpvKtPackage(),
+ MpvKtPreviewPackage(),
+ // added by plugins
+ // ...
)
init {
@@ -79,6 +97,50 @@ abstract class VideoClickAction {
/** Determines which plugin a given provider is from. This is the full path to the plugin. */
var sourcePlugin: String? = null
+ /** Even if VideoClickAction should not run any UI code, startActivity requires it,
+ * this is a wrapper for runOnUiThread in a suspended safe context that bubble up exceptions */
+ @Throws
+ suspend fun uiThread(callable : Callable) : T? {
+ val future = FutureTask{
+ try {
+ Result.success(callable.call())
+ } catch (t : Throwable) {
+ Result.failure(t)
+ }
+ }
+ CommonActivity.activity?.runOnUiThread(future) ?: throw ErrorLoadingException("No UI Activity, this should never happened")
+ val result = withContext(Dispatchers.IO) {
+ return@withContext future.get()
+ }
+ return result.getOrThrow()
+ }
+
+ /** Internally uses activityResultLauncher,
+ * use this when the activity has a result like watched position */
+ @Throws
+ suspend fun launchResult(intent : Intent?, options : ActivityOptionsCompat? = null) {
+ if (intent == null) {
+ return
+ }
+
+ uiThread {
+ MainActivity.activityResultLauncher?.launch(intent,options)
+ }
+ }
+
+ /** Internally uses startActivity, use this when you don't
+ * have any result that needs to be stored when exiting the activity */
+ @Throws
+ suspend fun launch(intent : Intent?, bundle : Bundle? = null) {
+ if (intent == null) {
+ return
+ }
+
+ uiThread {
+ CommonActivity.activity?.startActivity(intent, bundle)
+ }
+ }
+
fun uniqueId() = "$sourcePlugin:${this::class.jvmName}"
@Throws
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
index fa45aed8f..382f7d89a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.actions.temp
import android.app.Activity
import android.content.Context
import android.content.Intent
+import android.net.Uri
import androidx.core.net.toUri
import com.lagradost.api.Log
import com.lagradost.cloudstream3.actions.OpenInAppAction
@@ -30,7 +31,7 @@ open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv
packageName,
"is.xyz.mpv.MPVActivity"
) {
-
+ override val oneSource = true // mpv has poor playlist support on TV
override suspend fun putExtra(
context: Context,
intent: Intent,
@@ -42,7 +43,11 @@ open class MpvPackage(appName: String = "MPV", packageName: String = "is.xyz.mpv
putExtra("subs", result.subs.map { it.url.toUri() }.toTypedArray())
putExtra("title", video.name)
- makeTempM3U8Intent(context, this, result)
+ if (index != null) {
+ setDataAndType(Uri.parse(result.links.getOrNull(index)?.url ?: return), "video/*")
+ } else {
+ makeTempM3U8Intent(context, this, result)
+ }
val position = getViewPos(video.id)?.position
if (position != null)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
index 1e11281a5..1558bfd6a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -34,6 +34,6 @@ class PlayInBrowserAction: VideoClickAction() {
val link = result.links.getOrNull(index ?: 0) ?: return
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse(link.url)
- context?.startActivity(i)
+ launch(i)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
index fa7fc1173..b69562dd1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
@@ -25,6 +25,6 @@ class ViewM3U8Action: VideoClickAction() {
if (context == null) return
val i = Intent(Intent.ACTION_VIEW)
makeTempM3U8Intent(context, i, result)
- context.startActivity(i)
+ launch(i)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
index 8d65acf7e..7da76dbd7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
@@ -51,6 +51,7 @@ import com.google.android.gms.common.wrappers.Wrappers
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis
+import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
@@ -645,7 +646,7 @@ object AppContextUtils {
url: String,
fallbackWebview: Boolean = false,
fragment: Fragment? = null,
- ) {
+ ) = (this.getActivity() ?: activity)?.runOnUiThread {
try {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
@@ -677,10 +678,12 @@ object AppContextUtils {
}
fun Context.isNetworkAvailable(): Boolean {
- val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val connectivityManager =
+ getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val network = connectivityManager.activeNetwork ?: return false
- val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
+ val networkCapabilities =
+ connectivityManager.getNetworkCapabilities(network) ?: return false
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
} else {
@Suppress("DEPRECATION")
From f4fd30330688d1043c7b353212712a2fd22f0685 Mon Sep 17 00:00:00 2001
From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com>
Date: Sun, 29 Sep 2024 15:14:59 +0000
Subject: [PATCH 010/956] Fix fcast bottom dialog (#1357)
---
.../actions/temp/fcast/FcastAction.kt | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
index f69da0e05..458d1dc99 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -34,14 +34,16 @@ class FcastAction: VideoClickAction() {
) {
val link = result.links.getOrNull(index ?: 0) ?: return
val devices = FcastManager.currentDevices.toList()
- context?.getActivity()?.showBottomDialog(
- devices.map { it.name },
- -1,
- txt(R.string.player_settings_select_cast_device).asString(context),
- false,
- {}) {
- val position = getViewPos(video.id)?.position
- castTo(devices.getOrNull(it), link, position)
+ uiThread {
+ context?.getActivity()?.showBottomDialog(
+ devices.map { it.name },
+ -1,
+ txt(R.string.player_settings_select_cast_device).asString(context),
+ false,
+ {}) {
+ val position = getViewPos(video.id)?.position
+ castTo(devices.getOrNull(it), link, position)
+ }
}
}
From 3c385ea5e47b804706b66d08b49238dcfd181341 Mon Sep 17 00:00:00 2001
From: Jay Rathod <54902986+zzjjaayy@users.noreply.github.com>
Date: Wed, 2 Oct 2024 01:38:40 +0530
Subject: [PATCH 011/956] fix accidental motion in player when accessing
systemBars (#1358)
---
.../ui/player/FullScreenPlayer.kt | 40 +++++++++++++++----
1 file changed, 32 insertions(+), 8 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
index 9325f067f..70f7e3686 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
@@ -21,13 +21,14 @@ import android.view.MotionEvent
import android.view.Surface
import android.view.View
import android.view.ViewGroup
+import android.view.WindowInsets
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
import android.view.animation.AnimationUtils
-import androidx.annotation.OptIn
import android.widget.LinearLayout
+import androidx.annotation.OptIn
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
@@ -76,6 +77,7 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.round
+
const val MINIMUM_SEEK_TIME = 7000L // when swipe seeking
const val MINIMUM_VERTICAL_SWIPE = 2.0f // in percentage
const val MINIMUM_HORIZONTAL_SWIPE = 2.0f // in percentage
@@ -202,12 +204,34 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
throw NotImplementedError()
}
- /** Returns false if the touch is on the status bar or navigation bar*/
- private fun isValidTouch(rawX: Float, rawY: Float): Boolean {
- val statusHeight = statusBarHeight ?: 0
- // val navHeight = navigationBarHeight ?: 0
- // nav height is removed because screenWidth already takes into account that
- return rawY > statusHeight && rawX < screenWidth //- navHeight
+ /**
+ * [isValidTouch] should be called on a [View] spanning across the screen for reliable results.
+ *
+ * Android has supported gesture navigation properly since API-30. We get the absolute screen dimens using
+ * [WindowManager.getCurrentWindowMetrics] and remove the stable insets
+ * {[WindowInsets.getInsetsIgnoringVisibility]} to get a safe perimeter.
+ * This approach supports any and all types of necessary system insets.
+ *
+ * @return false if the touch is on the status bar or navigation bar
+ * */
+ private fun View.isValidTouch(rawX: Float, rawY: Float): Boolean {
+ // NOTE: screenWidth is without the navbar width when 3button nav is turned on.
+ if(Build.VERSION.SDK_INT >= 30) {
+ // real = absolute dimen without any default deductions like navbar width
+ val windowMetrics = (context?.getSystemService(Context.WINDOW_SERVICE) as? WindowManager)?.currentWindowMetrics
+ val realScreenHeight = windowMetrics?.let { windowMetrics.bounds.bottom - windowMetrics.bounds.top } ?: screenHeight
+ val realScreenWidth = windowMetrics?.let { windowMetrics.bounds.right - windowMetrics.bounds.left } ?: screenWidth
+
+ val insets = rootWindowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
+ val isOutsideHeight = rawY < insets.top || rawY > (realScreenHeight - insets.bottom)
+ val isOutsideWidth = if(windowMetrics == null) rawX < screenWidth
+ else rawX < insets.left || rawX > (realScreenWidth - insets.right)
+
+ return !(isOutsideWidth || isOutsideHeight)
+ } else {
+ val statusHeight = statusBarHeight ?: 0
+ return rawY > statusHeight && rawX < screenWidth
+ }
}
override fun exitedPipMode() {
@@ -937,7 +961,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
// validates if the touch is inside of the player area
- isCurrentTouchValid = isValidTouch(currentTouch.x, currentTouch.y)
+ isCurrentTouchValid = view.isValidTouch(currentTouch.x, currentTouch.y)
/*if (isCurrentTouchValid && player_episode_list?.isVisible == true) {
player_episode_list?.isVisible = false
} else*/ if (isCurrentTouchValid) {
From 3a657d7bfcd75b4f454838d1a1eb665c1bea4be8 Mon Sep 17 00:00:00 2001
From: Jay Rathod <54902986+zzjjaayy@users.noreply.github.com>
Date: Wed, 2 Oct 2024 01:42:04 +0530
Subject: [PATCH 012/956] #1302 Option to not show exit confirmation dialog
(#1345)
---
.../lagradost/cloudstream3/MainActivity.kt | 55 ++++++++++++-------
.../ui/settings/SettingsFragment.kt | 8 ++-
.../cloudstream3/ui/settings/SettingsUI.kt | 49 ++++++++++++-----
.../main/res/drawable/ic_baseline_exit_24.xml | 5 ++
.../main/res/layout/confirm_exit_dialog.xml | 13 +++++
app/src/main/res/values/array.xml | 11 ++++
app/src/main/res/values/strings.xml | 7 +++
app/src/main/res/xml/settings_general.xml | 2 -
app/src/main/res/xml/settings_ui.xml | 5 ++
9 files changed, 117 insertions(+), 38 deletions(-)
create mode 100644 app/src/main/res/drawable/ic_baseline_exit_24.xml
create mode 100644 app/src/main/res/layout/confirm_exit_dialog.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index fa54545cf..7ea399dac 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -1,9 +1,11 @@
package com.lagradost.cloudstream3
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import android.content.SharedPreferences
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Rect
@@ -18,6 +20,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
+import android.widget.CheckBox
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
@@ -29,7 +32,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.view.GravityCompat
import androidx.core.view.children
import androidx.core.view.isGone
import androidx.core.view.isInvisible
@@ -64,7 +66,6 @@ import com.lagradost.cloudstream3.APIHolder.initAll
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
-import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.loadThemes
import com.lagradost.cloudstream3.CommonActivity.onColorSelectedEvent
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
@@ -169,7 +170,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
@@ -613,14 +613,31 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
onUserLeaveHint(this)
}
- private fun showConfirmExitDialog() {
- val builder: AlertDialog.Builder = AlertDialog.Builder(this)
- builder.setTitle(R.string.confirm_exit_dialog)
- builder.apply {
- // Forceful exit since back button can actually go back to setup
- setPositiveButton(R.string.yes) { _, _ -> exitProcess(0) }
- setNegativeButton(R.string.no) { _, _ -> }
+ @SuppressLint("ApplySharedPref") // commit since the op needs to be synchronous
+ private fun showConfirmExitDialog(settingsManager: SharedPreferences) {
+ val confirmBeforeExit = settingsManager.getInt(getString(R.string.confirm_exit_key), -1)
+ when(confirmBeforeExit) {
+ // AUTO - Confirm exit is shown only on TV or EMULATOR by default
+ -1 -> if(isLayout(PHONE)) exitProcess(0)
+ // DON'T SHOW
+ 1 -> exitProcess(0)
+ // 0 -> SHOW
+ else -> { /*NO-OP : Continue*/ }
}
+
+ val dialogView = layoutInflater.inflate(R.layout.confirm_exit_dialog, null)
+ val dontShowAgainCheck: CheckBox = dialogView.findViewById(R.id.checkboxDontShowAgain)
+ val builder: AlertDialog.Builder = AlertDialog.Builder(this)
+ builder.setView(dialogView)
+ .setTitle(R.string.confirm_exit_dialog)
+ .setNegativeButton(R.string.no) { _, _ -> /*NO-OP*/}
+ .setPositiveButton(R.string.yes) { _, _ ->
+ if(dontShowAgainCheck.isChecked) {
+ settingsManager.edit().putInt(getString(R.string.confirm_exit_key), 1).commit()
+ }
+ exitProcess(0)
+ }
+
builder.show().setDefaultFocus()
}
@@ -1521,16 +1538,14 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
}
}
- if (isLayout(TV or EMULATOR)) {
- if (navDestination.matchDestination(R.id.navigation_home)) {
- attachBackPressedCallback {
- showConfirmExitDialog()
- window?.navigationBarColor =
- colorFromAttribute(R.attr.primaryGrayBackground)
- updateLocale()
- }
- } else detachBackPressedCallback()
- }
+ if (navDestination.matchDestination(R.id.navigation_home)) {
+ attachBackPressedCallback {
+ showConfirmExitDialog(settingsManager)
+ window?.navigationBarColor =
+ colorFromAttribute(R.attr.primaryGrayBackground)
+ updateLocale()
+ }
+ } else detachBackPressedCallback()
}
//val navController = findNavController(R.id.nav_host_fragment)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index 88335eeaf..339fe752d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -69,12 +69,16 @@ class SettingsFragment : Fragment() {
}
/**
- * Hide the Preference on selected layouts.
+ * Hide the [Preference] on selected layouts.
+ * @return [Preference] if visible otherwise null.
+ *
+ * [hideOn] is usually followed by some actions on the preference which are mostly
+ * unnecessary when the preference is disabled for the said layout thus returning null.
**/
fun Preference?.hideOn(layoutFlags: Int): Preference? {
if (this == null) return null
this.isVisible = !isLayout(layoutFlags)
- return this
+ return if(this.isVisible) this else null
}
/**
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
index 8c3ad0ade..014bd93fa 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
@@ -67,21 +67,23 @@ class SettingsUI : PreferenceFragmentCompat() {
settingsManager.getInt(getString(R.string.app_layout_key), -1)
activity?.showBottomDialog(
- prefNames.toList(),
- prefValues.indexOf(currentLayout),
- getString(R.string.app_layout),
- true,
- {}) {
- try {
- settingsManager.edit()
- .putInt(getString(R.string.app_layout_key), prefValues[it])
- .apply()
- context?.updateTv()
- activity?.recreate()
- } catch (e: Exception) {
- logError(e)
+ items = prefNames.toList(),
+ selectedIndex = prefValues.indexOf(currentLayout),
+ name = getString(R.string.app_layout),
+ showApply = true,
+ dismissCallback = {},
+ callback = {
+ try {
+ settingsManager.edit()
+ .putInt(getString(R.string.app_layout_key), prefValues[it])
+ .apply()
+ context?.updateTv()
+ activity?.recreate()
+ } catch (e: Exception) {
+ logError(e)
+ }
}
- }
+ )
return@setOnPreferenceClickListener true
}
@@ -187,5 +189,24 @@ class SettingsUI : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
+ getPref(R.string.confirm_exit_key)?.setOnPreferenceClickListener {
+ val prefNames = resources.getStringArray(R.array.confirm_exit)
+ val prefValues = resources.getIntArray(R.array.confirm_exit_values)
+ val confirmExit = settingsManager.getInt(getString(R.string.confirm_exit_key), -1)
+
+ activity?.showBottomDialog(
+ items = prefNames.toList(),
+ selectedIndex = prefValues.indexOf(confirmExit),
+ name = getString(R.string.confirm_before_exiting_title),
+ showApply = true,
+ dismissCallback = {},
+ callback = { selectedOption ->
+ settingsManager.edit()
+ .putInt(getString(R.string.confirm_exit_key), prefValues[selectedOption])
+ .apply()
+ }
+ )
+ return@setOnPreferenceClickListener true
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_baseline_exit_24.xml b/app/src/main/res/drawable/ic_baseline_exit_24.xml
new file mode 100644
index 000000000..bf421c227
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_exit_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/confirm_exit_dialog.xml b/app/src/main/res/layout/confirm_exit_dialog.xml
new file mode 100644
index 000000000..518aaa477
--- /dev/null
+++ b/app/src/main/res/layout/confirm_exit_dialog.xml
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml
index a987420e9..9a2438d47 100644
--- a/app/src/main/res/values/array.xml
+++ b/app/src/main/res/values/array.xml
@@ -222,6 +222,17 @@
- 2
+
+ - @string/automatic
+ - @string/show
+ - @string/dont_show
+
+
+ - -1
+ - 0
+ - 1
+
+
- Normal
- Dandelion Yellow
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 56f2465d2..144529ae8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -808,4 +808,11 @@
Seekbar preview
Enable preview thumbnail on seekbar
No subtitles loaded yet
+
+ Confirm before exiting
+ Show dialog before exiting the app
+ confirm_exit_key
+ Show
+ Don\'t Show
+
diff --git a/app/src/main/res/xml/settings_general.xml b/app/src/main/res/xml/settings_general.xml
index 853bbda19..4ddc54f33 100644
--- a/app/src/main/res/xml/settings_general.xml
+++ b/app/src/main/res/xml/settings_general.xml
@@ -6,8 +6,6 @@
android:title="@string/app_language"
android:icon="@drawable/ic_baseline_language_24" />
-
-
+
Date: Fri, 4 Oct 2024 19:41:39 +0000
Subject: [PATCH 013/956] Update player_custom_layout.xml (#1366)
---
app/src/main/res/layout/player_custom_layout.xml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml
index 3d78f0a60..412701c9f 100644
--- a/app/src/main/res/layout/player_custom_layout.xml
+++ b/app/src/main/res/layout/player_custom_layout.xml
@@ -170,24 +170,24 @@
Date: Sun, 13 Oct 2024 23:15:03 +0800
Subject: [PATCH 014/956] Added Filegram and Fix Vidguard Extractor (#1370)
---
.../cloudstream3/extractors/Filegram.kt | 63 +++++++++++++++
.../cloudstream3/extractors/Vidguard.kt | 76 +++++++++++--------
.../cloudstream3/utils/ExtractorApi.kt | 2 +
3 files changed, 109 insertions(+), 32 deletions(-)
create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Filegram.kt
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Filegram.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Filegram.kt
new file mode 100644
index 000000000..df7e03373
--- /dev/null
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Filegram.kt
@@ -0,0 +1,63 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.lagradost.cloudstream3.SubtitleFile
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.utils.ExtractorApi
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.M3u8Helper
+import com.lagradost.cloudstream3.utils.fixUrl
+import com.lagradost.cloudstream3.utils.getAndUnpack
+import org.jsoup.nodes.Element
+
+open class Filegram : ExtractorApi() {
+ override val name = "Filegram"
+ override val mainUrl = "https://filegram.to"
+ override val requiresReferer = true
+
+ override suspend fun getUrl(
+ url: String,
+ referer: String?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val header = mapOf(
+ "Accept" to "*/*",
+ "Accept-language" to "en-US,en;q=0.9",
+ "Origin" to mainUrl,
+ "Accept-Encoding" to "gzip, deflate, br, zstd",
+ "Connection" to "keep-alive",
+ "Sec-Fetch-Dest" to "empty",
+ "Sec-Fetch-Mode" to "cors",
+ "Sec-Fetch-Site" to "same-site",
+ "user-agent" to USER_AGENT,
+ )
+
+ val doc = app.get(getEmbedUrl(url), referer = referer).document
+ val unpackedJs = unpackJs(doc).toString()
+ val videoUrl = Regex("""file:\s*"([^"]+\.m3u8[^"]*)"""").find(unpackedJs)?.groupValues?.get(1)
+ if (videoUrl != null) {
+ M3u8Helper.generateM3u8(
+ this.name,
+ fixUrl(videoUrl),
+ "$mainUrl/",
+ headers = header
+ ).forEach(callback)
+ }
+ }
+
+ private fun unpackJs(script: Element): String? {
+ return script.select("script").find { it.data().contains("eval(function(p,a,c,k,e,d)") }
+ ?.data()?.let { getAndUnpack(it) }
+ }
+
+ private fun getEmbedUrl(url: String): String {
+ return if (!url.contains("/embed-")) {
+ val videoId = url.substringAfter("$mainUrl/")
+ "$mainUrl/embed-$videoId"
+ } else {
+ url
+ }
+ }
+
+}
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidguard.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidguard.kt
index eb4244a51..22f66936d 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidguard.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Vidguard.kt
@@ -12,7 +12,8 @@ import org.mozilla.javascript.Context
import org.mozilla.javascript.NativeJSON
import org.mozilla.javascript.NativeObject
import org.mozilla.javascript.Scriptable
-import java.util.Base64
+import kotlin.io.encoding.Base64
+import kotlin.io.encoding.ExperimentalEncodingApi
class Vidguardto1 : Vidguardto() {
override val mainUrl = "https://bembed.net"
@@ -37,8 +38,7 @@ open class Vidguardto : ExtractorApi() {
subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit
) {
- val embedUrl = url.replace("/d/", "/e/")
- val res = app.get(embedUrl)
+ val res = app.get(getEmbedUrl(url))
val resc = res.document.select("script:containsData(eval)").firstOrNull()?.data()
resc?.let {
val jsonStr2 = AppUtils.parseJson(runJS2(it))
@@ -57,41 +57,43 @@ open class Vidguardto : ExtractorApi() {
}
}
- private fun sigDecode(url: String): String {
+ @OptIn(ExperimentalEncodingApi::class)
+ private fun sigDecode(url: String): String {
val sig = url.split("sig=")[1].split("&")[0]
- var t = ""
- for (v in sig.chunked(2)) {
- val byteValue = Integer.parseInt(v, 16) xor 2
- t += byteValue.toChar()
- }
- val padding = when (t.length % 4) {
- 2 -> "=="
- 3 -> "="
- else -> ""
- }
- val decoded = Base64.getDecoder().decode((t + padding).toByteArray(Charsets.UTF_8))
- t = String(decoded).dropLast(5).reversed()
- val charArray = t.toCharArray()
- for (i in 0 until charArray.size - 1 step 2) {
- val temp = charArray[i]
- charArray[i] = charArray[i + 1]
- charArray[i + 1] = temp
- }
- val modifiedSig = String(charArray).dropLast(5)
- return url.replace(sig, modifiedSig)
+ val t = sig.chunked(2)
+ .joinToString("") { (Integer.parseInt(it, 16) xor 2).toChar().toString() }
+ .let {
+ val padding = when (it.length % 4) {
+ 2 -> "=="
+ 3 -> "="
+ else -> ""
+ }
+ String(Base64.decode((it + padding).toByteArray(Charsets.UTF_8)))
+ }
+ .dropLast(5)
+ .reversed()
+ .toCharArray()
+ .apply {
+ for (i in indices step 2) {
+ if (i + 1 < size) {
+ this[i] = this[i + 1].also { this[i + 1] = this[i] }
+ }
+ }
+ }
+ .concatToString()
+ .dropLast(5)
+ return url.replace(sig, t)
}
private fun runJS2(hideMyHtmlContent: String): String {
var result = ""
val r = Runnable {
- Log.d("runJS", "start")
val rhino = Context.enter()
rhino.initSafeStandardObjects()
rhino.optimizationLevel = -1
val scope: Scriptable = rhino.initSafeStandardObjects()
scope.put("window", scope, scope)
try {
- Log.d("runJS", "Executing JavaScript: $hideMyHtmlContent")
rhino.evaluateString(
scope,
hideMyHtmlContent,
@@ -101,27 +103,37 @@ open class Vidguardto : ExtractorApi() {
)
val svgObject = scope.get("svg", scope)
result = if (svgObject is NativeObject) {
- NativeJSON.stringify(Context.getCurrentContext(), scope, svgObject, null, null)
- .toString()
+ NativeJSON.stringify(
+ Context.getCurrentContext(),
+ scope,
+ svgObject,
+ null,
+ null
+ ).toString()
} else {
Context.toString(svgObject)
}
- Log.d("runJS", "Result: $result")
} catch (e: Exception) {
Log.e("runJS", "Error executing JavaScript: ${e.message}")
} finally {
Context.exit()
}
}
- val t = Thread(ThreadGroup("A"), r, "thread_rhino", 2000000)// StackSize 2Mb: Run in a thread because rhino requires more stack size for large scripts.
- t.start();
+ val t = Thread(ThreadGroup("A"), r, "thread_rhino", 8 * 1024 * 1024) // Increase stack size to 8MB
+ t.start()
t.join()
t.interrupt()
return result
}
+
+ private fun getEmbedUrl(url: String): String {
+ return url.takeIf { it.contains("/d/") }
+ ?.replace("/d/", "/e/") ?: url
+ }
data class SvgObject(
val stream: String,
val hash: String
- )
+ )
+
}
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 3244f9942..83e3c4f49 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -52,6 +52,7 @@ import com.lagradost.cloudstream3.extractors.FileMoon
import com.lagradost.cloudstream3.extractors.FileMoonIn
import com.lagradost.cloudstream3.extractors.FileMoonSx
import com.lagradost.cloudstream3.extractors.Filesim
+import com.lagradost.cloudstream3.extractors.Filegram
import com.lagradost.cloudstream3.extractors.Fplayer
import com.lagradost.cloudstream3.extractors.Geodailymotion
import com.lagradost.cloudstream3.extractors.GMPlayer
@@ -1011,6 +1012,7 @@ val extractorApis: MutableList = arrayListOf(
Boosterx(),
Ds2play(),
Ds2video(),
+ Filegram(),
)
From 54f22139d864840863d93f1588b5801e7987c2c7 Mon Sep 17 00:00:00 2001
From: Jay Rathod <54902986+zzjjaayy@users.noreply.github.com>
Date: Sun, 13 Oct 2024 21:34:07 +0530
Subject: [PATCH 015/956] feat: option to choose backup target directory
(#1354)
---
.../ui/settings/SettingsGeneral.kt | 50 ++++---------
.../ui/settings/SettingsUpdates.kt | 73 +++++++++++++++++--
.../ui/settings/utils/DirectoryPicker.kt | 27 +++++++
.../cloudstream3/utils/BackupUtils.kt | 53 +++++++++++++-
.../drawable/ic_baseline_folder_open_24.xml | 5 ++
app/src/main/res/values/strings.xml | 5 ++
app/src/main/res/xml/settings_updates.xml | 5 ++
7 files changed, 172 insertions(+), 46 deletions(-)
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/settings/utils/DirectoryPicker.kt
create mode 100644 app/src/main/res/drawable/ic_baseline_folder_open_24.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
index 7cb1a848f..da723b738 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
@@ -7,7 +7,6 @@ import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.Toast
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
@@ -35,6 +34,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hideOn
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
+import com.lagradost.cloudstream3.ui.settings.utils.getChooseFolderLauncher
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.isAppRestricted
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.showBatteryOptimizationDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
@@ -46,7 +46,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
-import com.lagradost.safefile.SafeFile
// Change local language settings in the app.
fun getCurrentLocale(context: Context): String {
@@ -146,34 +145,15 @@ class SettingsGeneral : PreferenceFragmentCompat() {
val lang: String,
)
- // Open file picker
- private val pathPicker =
- registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
- // It lies, it can be null if file manager quits.
- if (uri == null) return@registerForActivityResult
- val context = context ?: AcraApplication.context ?: return@registerForActivityResult
- // RW perms for the path
- val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-
- context.contentResolver.takePersistableUriPermission(uri, flags)
-
- val file = SafeFile.fromUri(context, uri)
- val filePath = file?.filePath()
- println("Selected URI path: $uri - Full path: $filePath")
-
- // Stores the real URI using download_path_key
- // Important that the URI is stored instead of filepath due to permissions.
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit().putString(getString(R.string.download_path_key), uri.toString()).apply()
-
- // From URI -> File path
- // File path here is purely for cosmetic purposes in settings
- (filePath ?: uri.toString()).let {
- PreferenceManager.getDefaultSharedPreferences(context)
- .edit().putString(getString(R.string.download_path_pref), it).apply()
- }
+ private val pathPicker = getChooseFolderLauncher { uri, path ->
+ val context = context ?: AcraApplication.context ?: return@getChooseFolderLauncher
+ (path ?: uri.toString()).let {
+ PreferenceManager.getDefaultSharedPreferences(context).edit()
+ .putString(getString(R.string.download_path_key), uri.toString())
+ .putString(getString(R.string.download_path_key_visual), it)
+ .apply()
}
+ }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()
@@ -367,11 +347,11 @@ class SettingsGeneral : PreferenceFragmentCompat() {
val dirs = getDownloadDirs()
val currentDir =
- settingsManager.getString(getString(R.string.download_path_pref), null)
+ settingsManager.getString(getString(R.string.download_path_key_visual), null)
?: context?.let { ctx -> VideoDownloadManager.getDefaultDir(ctx)?.filePath() }
activity?.showBottomDialog(
- dirs + listOf("Custom"),
+ dirs + listOf(getString(R.string.custom)),
dirs.indexOf(currentDir),
getString(R.string.download_path_pref),
true,
@@ -386,11 +366,11 @@ class SettingsGeneral : PreferenceFragmentCompat() {
} else {
// Sets both visual and actual paths.
// key = used path
- // pref = visual path
+ // visual = visual path
settingsManager.edit()
- .putString(getString(R.string.download_path_key), dirs[it]).apply()
- settingsManager.edit()
- .putString(getString(R.string.download_path_pref), dirs[it]).apply()
+ .putString(getString(R.string.download_path_key), dirs[it])
+ .putString(getString(R.string.download_path_key_visual), dirs[it])
+ .apply()
}
}
return@setOnPreferenceClickListener true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
index 260c66747..4c45480cd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
@@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.ui.settings
+import android.net.Uri
import android.os.Bundle
import android.view.View
import android.widget.Toast
@@ -14,13 +15,18 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.databinding.LogcatBinding
import com.lagradost.cloudstream3.mvvm.logError
+import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.services.BackupWorkManager
import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
+import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
+import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hideOn
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
+import com.lagradost.cloudstream3.ui.settings.utils.getChooseFolderLauncher
import com.lagradost.cloudstream3.utils.BackupUtils
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -37,7 +43,8 @@ import java.io.InputStreamReader
import java.io.OutputStream
import java.lang.System.currentTimeMillis
import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Date
+import java.util.Locale
class SettingsUpdates : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -47,10 +54,20 @@ class SettingsUpdates : PreferenceFragmentCompat() {
setToolBarScrollFlags()
}
+ private val pathPicker = getChooseFolderLauncher { uri, path ->
+ val context = context ?: AcraApplication.context ?: return@getChooseFolderLauncher
+ (path ?: uri.toString()).let {
+ PreferenceManager.getDefaultSharedPreferences(context).edit()
+ .putString(getString(R.string.backup_path_key), uri.toString())
+ .putString(getString(R.string.backup_dir_key), it)
+ .apply()
+ }
+ }
+
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()
setPreferencesFromResource(R.xml.settings_updates, rootKey)
- //val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
getPref(R.string.backup_key)?.setOnPreferenceClickListener {
BackupUtils.backup(activity)
@@ -58,8 +75,6 @@ class SettingsUpdates : PreferenceFragmentCompat() {
}
getPref(R.string.automatic_backup_key)?.setOnPreferenceClickListener {
- val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
-
val prefNames = resources.getStringArray(R.array.periodic_work_names)
val prefValues = resources.getIntArray(R.array.periodic_work_values)
val current = settingsManager.getInt(getString(R.string.automatic_backup_key), 0)
@@ -89,6 +104,38 @@ class SettingsUpdates : PreferenceFragmentCompat() {
activity?.restorePrompt()
return@setOnPreferenceClickListener true
}
+ getPref(R.string.backup_path_key)?.hideOn(TV or EMULATOR)?.setOnPreferenceClickListener {
+ val dirs = getBackupDirsForDisplay()
+ val currentDir =
+ settingsManager.getString(getString(R.string.backup_dir_key), null)
+ ?: context?.let { ctx -> BackupUtils.getDefaultBackupDir(ctx)?.filePath() }
+
+ activity?.showBottomDialog(
+ dirs + listOf(getString(R.string.custom)),
+ dirs.indexOf(currentDir),
+ getString(R.string.backup_path_title),
+ true,
+ {}) {
+ // Last = custom
+ if (it == dirs.size) {
+ try {
+ pathPicker.launch(Uri.EMPTY)
+ } catch (e: Exception) {
+ logError(e)
+ }
+ } else {
+ // Sets both visual and actual paths.
+ // path = used uri
+ // dir = dir path
+ settingsManager.edit()
+ .putString(getString(R.string.backup_path_key), dirs[it])
+ .putString(getString(R.string.backup_dir_key), dirs[it])
+ .apply()
+ }
+ }
+ return@setOnPreferenceClickListener true
+ }
+
getPref(R.string.show_logcat_key)?.setOnPreferenceClickListener { pref ->
val builder =
AlertDialog.Builder(pref.context, R.style.AlertDialogCustom)
@@ -156,8 +203,6 @@ class SettingsUpdates : PreferenceFragmentCompat() {
}
getPref(R.string.apk_installer_key)?.setOnPreferenceClickListener {
- val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context)
-
val prefNames = resources.getStringArray(R.array.apk_installer_pref)
val prefValues = resources.getIntArray(R.array.apk_installer_values)
@@ -196,8 +241,6 @@ class SettingsUpdates : PreferenceFragmentCompat() {
}
getPref(R.string.auto_download_plugins_key)?.setOnPreferenceClickListener {
- val settingsManager = PreferenceManager.getDefaultSharedPreferences(it.context)
-
val prefNames = resources.getStringArray(R.array.auto_download_plugin)
val prefValues =
enumValues().sortedBy { x -> x.value }.map { x -> x.value }
@@ -217,4 +260,18 @@ class SettingsUpdates : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
}
+
+ private fun getBackupDirsForDisplay(): List {
+ return normalSafeApiCall {
+ context?.let { ctx ->
+ val defaultDir = BackupUtils.getDefaultBackupDir(ctx)?.filePath()
+ val first = listOf(defaultDir)
+ (runCatching {
+ first + BackupUtils.getCurrentBackupDir(ctx).let {
+ it.first?.filePath() ?: it.second
+ }
+ }.getOrNull() ?: first).filterNotNull().distinct()
+ }
+ } ?: emptyList()
+ }
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/utils/DirectoryPicker.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/utils/DirectoryPicker.kt
new file mode 100644
index 000000000..9e126b7a6
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/utils/DirectoryPicker.kt
@@ -0,0 +1,27 @@
+package com.lagradost.cloudstream3.ui.settings.utils
+
+import android.content.Intent
+import android.net.Uri
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.fragment.app.Fragment
+import com.lagradost.cloudstream3.AcraApplication
+import com.lagradost.safefile.SafeFile
+
+fun Fragment.getChooseFolderLauncher(dirSelected: (uri: Uri?, path: String?) -> Unit) =
+ registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
+ // It lies, it can be null if file manager quits.
+ if (uri == null) return@registerForActivityResult
+ val context = context ?: AcraApplication.context ?: return@registerForActivityResult
+ // RW perms for the path
+ val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+
+ context.contentResolver.takePersistableUriPermission(uri, flags)
+
+ val filePath = SafeFile.fromUri(context, uri)?.filePath()
+ println("Selected URI path: $uri - Full path: $filePath")
+
+ // store the actual URI instead of the path due to permissions.
+ // filePath should only be used for cosmetic purposes.
+ dirSelected(uri, filePath)
+ }
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
index b25be59f9..a44af8773 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
@@ -7,7 +7,9 @@ import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.WorkerThread
+import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
+import androidx.preference.PreferenceManager
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
@@ -35,8 +37,14 @@ import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs
import com.lagradost.cloudstream3.utils.DataStore.mapper
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
+import com.lagradost.cloudstream3.utils.VideoDownloadManager.StreamData
+import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
import com.lagradost.cloudstream3.utils.VideoDownloadManager.setupStream
+import com.lagradost.safefile.MediaFileContentType
+import com.lagradost.safefile.SafeFile
import okhttp3.internal.closeQuietly
+import java.io.File
+import java.io.IOException
import java.io.OutputStream
import java.io.PrintWriter
import java.lang.System.currentTimeMillis
@@ -69,7 +77,12 @@ object BackupUtils {
"biometric_key", // can lock down users if backup is shared on a incompatible device
"nginx_user", // Nginx user key
- "download_path_key" // No access rights after restore data from backup
+
+ // No access rights after restore data from backup
+ "download_path_key",
+ "download_path_key_visual",
+ "backup_path_key",
+ "backup_dir_path_key"
)
/** false if key should not be contained in backup */
@@ -166,10 +179,9 @@ object BackupUtils {
}
val date = SimpleDateFormat("yyyy_MM_dd_HH_mm").format(Date(currentTimeMillis()))
- val ext = "txt"
val displayName = "CS3_Backup_${date}"
val backupFile = getBackup(context)
- val stream = setupStream(context, displayName, null, ext, false)
+ val stream = setupBackupStream(context, displayName)
fileStream = stream.openNew()
printStream = PrintWriter(fileStream)
@@ -195,6 +207,18 @@ object BackupUtils {
}
}
+ @Throws(IOException::class)
+ private fun setupBackupStream(context: Context, name: String, ext: String = "txt"): StreamData {
+ return setupStream(
+ baseFile = getCurrentBackupDir(context).first ?: getDefaultBackupDir(context)
+ ?: throw IOException("Bad config"),
+ name,
+ folder = null,
+ extension = ext,
+ tryResume = false
+ )
+ }
+
fun FragmentActivity.setUpBackup() {
try {
restoreFileSelector =
@@ -264,4 +288,27 @@ object BackupUtils {
}
editor.apply()
}
+
+ /**
+ * Copy of [VideoDownloadManager.basePathToFile], [VideoDownloadManager.getDefaultDir] and [VideoDownloadManager.getBasePath]
+ * modded for backup specific paths
+ * */
+
+ fun getDefaultBackupDir(context: Context): SafeFile? {
+ return SafeFile.fromMedia(context, MediaFileContentType.Downloads)
+ }
+
+ fun getCurrentBackupDir(context: Context): Pair {
+ val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
+ val basePathSetting = settingsManager.getString(context.getString(R.string.backup_path_key), null)
+ return baseBackupPathToFile(context, basePathSetting) to basePathSetting
+ }
+
+ private fun baseBackupPathToFile(context: Context, path: String?): SafeFile? {
+ return when {
+ path.isNullOrBlank() -> getDefaultBackupDir(context)
+ path.startsWith("content://") -> SafeFile.fromUri(context, path.toUri())
+ else -> SafeFile.fromFile(context, File(path))
+ }
+ }
}
diff --git a/app/src/main/res/drawable/ic_baseline_folder_open_24.xml b/app/src/main/res/drawable/ic_baseline_folder_open_24.xml
new file mode 100644
index 000000000..6e130c3c9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_folder_open_24.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 144529ae8..30c2a9498 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -46,6 +46,7 @@
dns_key
jsdelivr_proxy_key
download_path_key
+ download_path_key_visual
Cloudstream
app_layout_key
primary_color_key
@@ -808,6 +809,10 @@
Seekbar preview
Enable preview thumbnail on seekbar
No subtitles loaded yet
+ backup_path_key
+ Backup folder location
+ backup_dir_key
+ Custom
Confirm before exiting
Show dialog before exiting the app
diff --git a/app/src/main/res/xml/settings_updates.xml b/app/src/main/res/xml/settings_updates.xml
index 102f8ee4b..741a62a7a 100644
--- a/app/src/main/res/xml/settings_updates.xml
+++ b/app/src/main/res/xml/settings_updates.xml
@@ -46,6 +46,11 @@
android:icon="@drawable/baseline_restore_page_24"
android:key="@string/restore_key"
android:title="@string/restore_settings" />
+
+
Date: Sun, 13 Oct 2024 19:24:46 +0200
Subject: [PATCH 016/956] Translations update from Hosted Weblate (#1348)
Co-authored-by: Anarchydr
Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com>
Co-authored-by: Dan
Co-authored-by: Fjuro
Co-authored-by: James Sands
Co-authored-by: Jose Delvani
Co-authored-by: Kyivska Hryvnya
Co-authored-by: Massimo Pissarello
Co-authored-by: Matthaiks
Co-authored-by: Pizza Party
Co-authored-by: PumbaLP Galant
Co-authored-by: Rex_sa
Co-authored-by: Sanloz
Co-authored-by: Tiago Lucas
Co-authored-by: gallegonovato
---
app/src/main/res/values-ajp/strings.xml | 12 ++--
app/src/main/res/values-ar/strings.xml | 4 ++
app/src/main/res/values-bp/strings.xml | 4 ++
app/src/main/res/values-cs/strings.xml | 4 ++
app/src/main/res/values-de/strings.xml | 7 +-
app/src/main/res/values-es/strings.xml | 4 ++
app/src/main/res/values-fr/strings.xml | 3 +-
app/src/main/res/values-hr/strings.xml | 10 ++-
app/src/main/res/values-in/strings.xml | 1 -
app/src/main/res/values-it/strings.xml | 92 +++++++++++++------------
app/src/main/res/values-pl/strings.xml | 4 ++
app/src/main/res/values-pt/strings.xml | 9 ++-
app/src/main/res/values-qt/strings.xml | 13 +++-
app/src/main/res/values-ru/strings.xml | 5 +-
app/src/main/res/values-sv/strings.xml | 1 -
app/src/main/res/values-tr/strings.xml | 1 -
app/src/main/res/values-uk/strings.xml | 6 +-
app/src/main/res/values-vi/strings.xml | 8 ++-
18 files changed, 121 insertions(+), 67 deletions(-)
diff --git a/app/src/main/res/values-ajp/strings.xml b/app/src/main/res/values-ajp/strings.xml
index 0b30cebac..8b442c831 100644
--- a/app/src/main/res/values-ajp/strings.xml
+++ b/app/src/main/res/values-ajp/strings.xml
@@ -6,7 +6,7 @@
التنزيلات
%1$sالحلقة %2$d
محي الملف
- سوري، الآپ تعطل. رح ينبعت تقرير عن المشكلة للمطورين.
+ سوري، الآپ تعطل. رح ينبعت تقرير عن المشكلة للمطورين
%1$d %2$s
بس بعات البيانات وقت ما يتعطل الآپ
شوف الخلفية
@@ -443,7 +443,7 @@
التلخيص
إِيه
الرئيسي
- سكر الآپ حتى تطبق التغيرات
+ سكر الآپ حتى تطبق التغيرات.
ساعدوني
عوز هيدا إذا عم بتبين الترجمة %d ميلي ثانية بعد ما لازم
م نلاقا الآپ
@@ -452,7 +452,7 @@
مقترح
TS
فتاح من الإنترنت
- غَبر شكل الآپ حتى يتناسب مع جهازك
+ غَير شكل الآپ حتى يتناسب مع جهازك
مطفي: %d
وقف
هيدي للايحة فاضية. جربو تبدلو ع غيرا.
@@ -637,7 +637,7 @@
بلشه من الأول
تحذير
فتاح الڤيديو اللي ع جهازك
- مافي تنزيلات هلّق
+ مافي تنزيلات هلّق.
محي الإضافة
محي اسامي كبسات صيطرة مشغل الڤيديو
نهار اللي نزل (من جديد ل قديم)
@@ -663,4 +663,8 @@
صورة زغيرة مع التقريب وال تبعيد
بت حط صورة زغير من الڤيديو إنت و عم بت قرب أو ترجع بال ڤيديو
بعد مش معمول لود لولا ترجمة
+ فرجي
+ م تفرجي
+ طلوب تأكيد قبلما تطلع من الآپ
+ أكّد إنو بدك تطلع
\ No newline at end of file
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 56bb4b4a0..f32335263 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -689,4 +689,8 @@
معاينة شريط البحث
تمكين معاينة الصورة المصغرة على شريط البحث
لم يتم تحميل أي ترجمات بعد
+ تأكيد قبل الخروج
+ عرض مربع الحوار قبل الخروج من التطبيق
+ أظهِر
+ لا تظهر
\ No newline at end of file
diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml
index 55e1ce180..0b50327ae 100644
--- a/app/src/main/res/values-bp/strings.xml
+++ b/app/src/main/res/values-bp/strings.xml
@@ -679,4 +679,8 @@
Ativar visualização de miniatura na barra de busca
Visualização da barra de busca
Ainda não há legendas carregadas
+ Mostrar
+ Confirmar antes de sair
+ Mostrar caixa de diálogo antes de sair do aplicativo
+ Não mostrar
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index ba03b05fb..104017444 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -681,4 +681,8 @@
Náhled v liště přehrávače
Povolit náhled miniatur na liště přehrávače
Zatím nenačteny žádné titulky
+ Potvrdit před ukončením
+ Zobrazit dialog před ukončením aplikace
+ Zobrazit
+ Nezobrazovat
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 6cf8ff3bd..a0774097c 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -390,8 +390,8 @@
Fertig
Erweiterungen
Repository hinzufügen
- Repository-Name
- Repository-URL
+ Repository Name (Optional)
+ Repository-URL oder Kurz Code
Plugin geladen
Plugin gelöscht
Fehler beim Laden von %s
@@ -652,4 +652,7 @@
\n%s
Veröffentlichungsdatum (von neu nach alt)
Veröffentlichungsdatum (von alt nach neu)
+ Suchleisten Vorschau
+ Vorschau Bilder in der Suchleiste einschalten
+ Keine Untertitel geladen
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index a0c4587f5..32453ec14 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -657,4 +657,8 @@
Activar la previsualización para las miniaturas en la barra de búsqueda
Previsualización de Seekbar
Aún no hay subtítulos cargados
+ Mostrar el diálogo antes de salir de la aplicación
+ Mostrar
+ No mostrar
+ Confirmar antes de salir
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 5b3f09120..e446b6c19 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -138,7 +138,7 @@
DNS avec HTTPS
Afficher les animés en Anglais (Dub) / sous-titrés
Disposition en mode téléphone
- episode_action_copy_link
+ %1$s Ep %2$d
Note : %.1f
Zoom
Adapter à l\'écran
@@ -628,7 +628,6 @@
Ouvrir le dépôt
Code expire dans %1$dm %2$ds
Wiki de CloudStream
- MPV YTDL
Le code PIN est maintenant expiré !
Image du code QR
Supprimer l\'extension
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index dcacbeee3..8fc2850c6 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -411,8 +411,8 @@
Gotovo
Proširenja
Dodaj repozitorij
- Ime repozitorija
- URL repozitorija
+ Ime repozitorija (Neobavezno)
+ URL repozitorija ili kratki kod
Dodatak učitan
Dodatak izbrisan
Nije moguće učitati %s
@@ -680,4 +680,10 @@
\n
\n%s
Još nije učitan nijedan titl
+ Pretpregled trake za traženje
+ Omogući minijaturu pregleda na traci za pretraživanje
+ Potvrdi prije izlaza
+ Prikaži dijaloški okvir prije izlaska iz aplikacije
+ Prikaži
+ Ne prikazuj
\ No newline at end of file
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index efd0ed0cf..f7ab78f0f 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -647,7 +647,6 @@
Mengabaikan
Kode kedaluwarsa dalam %1$dm %2$ds
Otorisasi Lokal
- MPV YTDL
Kunjungi %s di ponsel pintar atau komputer Anda dan masukkan kode di atas
Pratinjau bilah pencarian
Aktifkan pratinjau gambar mini di bilah pencarian
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 590c167d9..7955b8ca3 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -134,12 +134,12 @@
Tocca due volte il lato destro o sinistro dello schermo per mandare avanti o indietro il video
Tocca due volte al centro per mettere in pausa
Intervallo di ricerca lettore (secondi)
- Utilizzare la luminosità del sistema
- Utilizzare la luminosità del sistema al posto di una sovrapposizione scura
+ Usa luminosità del sistema
+ Utilizza la luminosità del sistema nel lettore dell\'app anziché una sovrapposizione scura
Sincronizza automaticamente gli episodi guardati
- Ripristinare i dati da backup
- Backup data
+ Ripristina dati da un backup
+ Backup dei dati
File di backup caricato
Impossibile ripristinare i dati dal file %s
Dati salvati
@@ -187,7 +187,7 @@
Nessun episodio trovato
Elimina file
Elimina
- Cancella
+ Annulla
Pausa
Riprendi
-30
@@ -218,7 +218,7 @@
OVA
Torrent
Documentari
- Drama Asiatici
+ Drammi asiatici
Livestreams
NSFW
Altri
@@ -230,34 +230,34 @@
OVA
Torrent
Documentario
- Drama Asiatico
+ Dramma asiatico
Livestream
NSFW
Altro
- Source error
+ Errore sorgente
Errore remoto
Errore del renderer
- Errore inaspettato nel player video
+ Errore imprevisto del lettore
Errore download, controlla i permessi di archiviazione
Episodio Chromecast
Mirror Chromecast
- Riproduci in app
+ Riproduci nell\'app
Riproduci in %s
- Download
+ Download automatico
Mirror download
- Aggiorna link
+ Ricarica link
Download sottotitoli
- Etichetta Qualità
- Etichetta Dub
- Etichetta Sub
+ Etichetta qualità
+ Etichetta dub
+ Etichetta sub
Titolo
- Etichette sui Poster
+ Attiva/disattiva elementi dell\'UI sul poster
Nessun aggiornamento trovato
Controlla aggiornamenti
Blocca
Ridimensiona
Sorgente
- Salta intro
+ Salta OP
Non mostrare di nuovo
Salta questo aggiornamento
Aggiorna
@@ -270,12 +270,12 @@
Cancella cache immagini e video
Causa arresti anomali se impostato troppo alto sui dispositivi con poca memoria, come Android TV.
Se impostato troppo alto può causare problemi su sistemi con poca archiviazione interna, come i dispositivi Android TV.
- DNS over HTTPS
- Utile per bypassare i blocchi ISP
+ DNS su HTTPS
+ Utile per aggirare i blocchi ISP
Clona un sito
Rimuovi sito
Aggiungi un clone di un sito esistente, con un URL differente
- Posizione Download
+ Percorso di download
URL server NGINX
Mostra anime doppiati/sottotitolati
Adatta allo schermo
@@ -306,7 +306,7 @@
hello@world.com
127.0.0.1
NuovoNomeSito
- https://example.com
+ https://esempio.com
Codice lingua (it)
%1$s %2$s
account
@@ -323,7 +323,7 @@
/??
/%d
%s autenticato
- Impossibile autenticarsi a %s
+ Impossibile effettuare l\'accesso a %s
Nessuno
Normale
@@ -337,8 +337,8 @@
Sincronizza sottotitoli
1000 ms
Ritardo sottotitoli
- Usa se i sottotitoli sono mostrati %d ms troppo presto
- Usa se i sottotitoli vengono mostrati %d ms troppo tardi
+ Usalo se i sottotitoli vengono mostrati %d ms in anticipo
+ Usalo se i sottotitoli vengono mostrati %d ms in ritardo
Nessun ritardo dei sottotitoli
Cancella cronologia
Cronologia
- Mostra popup per salta sigla iniziale/finale
+ Mostra popup di salto per apertura/fine
Testo troppo lungo. Impossibile salvare negli appunti.
CloudStream
Scarica automaticamente i plugins
Impossibile installare la nuova versione dell\'app
- Aggiorna il progresso di avanzamento
+ Aggiorna progresso di avanzamento
Opening misto
- Installando l\'aggiornamento dell\'app…
+ Installazione aggiornamento dell\'app…
Rimuovi le didascalie chiuse dai sottotitoli
Rimuovi il bloat dai sottotitoli
Opening
@@ -476,7 +476,7 @@
Sei sicuro di voler uscire?
Sì
No
- Scaricando l\'aggiornamento dell\'app…
+ Download aggiornamento dell\'app…
Installa automaticamente tutti i plugin non ancora installati dai repository aggiunti.
ProgrammaInstallazionePacchetti
Alcuni telefoni non supportano il nuovo programma di installazione dei pacchetti. Prova l\'opzione legacy se gli aggiornamenti non vengono installati.
@@ -536,7 +536,7 @@
Iscritto a %s
Impossibile raggiungere GitHub. Attivazione proxy jsDelivr…
Evita il blocco degli URL github non elaborati utilizzando jsDelivr. Potrebbe causare un ritardo degli aggiornamenti di alcuni giorni.
- Baypass ISP
+ Bypass ISP
Ripristina
Aggiornando shows a cui sei iscritto
L\'episodio %d è stato rilasciato!
@@ -575,27 +575,27 @@
Frequenza di backup
Trovato Possibile Duplicato
Aggiungi ai preferiti
- Rimpiazza tutti
+ Sostituisci tutto
PIN non corretto. Riprova.
Disiscriviti
Il PIN deve essere almeno di 4 caratteri
- Rimpiazza
+ Sostituisci
Aggiungi
Iscriviti
Rimuovi dai preferiti
- Seleziona un Account
+ Seleziona un account
Sembra che nella tua libreria esista già un elemento potenzialmente duplicato: \'%s.\'
\n
\nDesideri aggiungere comunque questo elemento, sostituire quello esistente o annullare l\'azione?
Inserisci PIN
PIN
- Inserisci PIN Corrente
- Entrato come %s
+ Inserisci PIN corrente
+ Connesso come %s
Inserisci il PIN per %s
Blocca profilo
Usa account predefinito
Salta la selezione dell\'account all\'avvio
- Gestisci Accounts
+ Gestisci account
Modifica account
Collegamenti ricaricati
Ruota
@@ -609,7 +609,7 @@
Questo test è pensato solo per gli sviluppatori e non verifica o nega il funzionamento di alcuna estensione.
Notifica nuovo episodio
Sblocca CloudStream
- Blocca con biometria
+ Blocco con dati biometrici
Autenticazione con password/PIN
L\'autenticazione biometrica non è supportata su questo dispositivo
Sblocca app con impronta digitale, Face ID, PIN, sequenza e password.
@@ -677,4 +677,8 @@
Anteprima barra di ricerca
Abilita miniatura di anteprima sulla barra di ricerca
Nessun sottotitolo caricato
+ Conferma prima di uscire
+ Mostra finestra di dialogo prima di uscire dall\'app
+ Mostra
+ Non mostrare
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 2f26c3f5b..6c1cb88bb 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -658,4 +658,8 @@
Podgląd paska przewijania
Włącz podgląd miniatury na pasku wyszukiwania
Nie wczytano jeszcze napisów
+ Pokaż okno dialogowe przed wyjściem z aplikacji
+ Pokaż
+ Potwierdź przed wyjściem
+ Nie pokazuj
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 7fdbc6bee..da4b1fc14 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -354,8 +354,8 @@
Feito
Extensões
Adicionar repositório
- Nome do repositório
- URL do repositório
+ Nome do repositório (Opcional)
+ URL do repositório or Referência
Plugin Carregado
Plugin Apagado
Falha ao carregar %s
@@ -622,7 +622,6 @@
Irá também eliminar permanentemente todos os episódios da seguinte série:
\n
\n%s
- MPV YTDL
Eliminar (%1$d | %2$s)
Visite %s no seu smartphone ou computador e introduza o código acima
Recomeçar
@@ -656,4 +655,8 @@
Ainda sem legendadas carregadas
Abrir repositório
Dispensar
+ Confirmar antes de sair
+ Mostrar diálogo antes de sair da aplicação
+ Mostrar
+ Não mostrar
\ No newline at end of file
diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml
index 8f0e14cbc..2ca5a5881 100644
--- a/app/src/main/res/values-qt/strings.xml
+++ b/app/src/main/res/values-qt/strings.xml
@@ -153,7 +153,7 @@
ohaooo-ahahouuhhh
ahohooh
aaaaaohaah ooh
- ahh haa ah ooooo
+ Boooooo ooooo
aaahhuooh ahh
aaaaa oooohh aauugghh
aaaghhahhooo-ahahahhha aaaghh
@@ -311,7 +311,7 @@
4K
oohh ah uuk aagg ag aah
uug uuuugggoog
- uuuuggguug uugg
+ uuuuggguug uugg (uuooal)
aaaahhhaah %1$d %2$s
aaahh uuuugggh
oooohh oooogggoog
@@ -604,7 +604,7 @@
aaagg oooogg og uuugg aaaagggag
aaak ooohh
uuuuuk aag aaak uh ooh aah oh oogg oohh uuuugg
- aaaaggguuh uug
+ aaaaggguuh uug o aaooolllo
uuuugg uuuuhhh
ooogg eek oogg %s
aaaagg
@@ -654,4 +654,11 @@
uuhh ug oooohhh
ooh oogg uh 4 uuuuhhhooh
uuuugg oh oooohhh
+ Oo hoo-hoo haaa-oo ar-ar
+ ee-ee-ar-ar pee-ee-oo-oo kik-kik oo kik-hoo
+ kik-kik-kik pee-ee-oo-oo
+ Boo kik-kik oo-chit ar-ar coo haa
+ Boo
+ Oo-ahh oo-chit ar-ar
+ Boooooo
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index c251ca9bb..5dd66d0f8 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -656,6 +656,9 @@
Войти локально
Предосмотр поисковой строки
Включить предосмотр миниатюр в поисковой строке
- MPV YTDL
Субтитры еще не загружены
+ Подтвердить перед выходом
+ Показать диалог перед выходом из апликации
+ Показать
+ Не показывать
\ No newline at end of file
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 06f388848..4670e807d 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -632,7 +632,6 @@
\n%2$s
Aktivera förhandsgranskningsminiatyr i sökfältet
Förhandsgranskning av sökfältet
- MPV YTDL
Spela från början
Releasedatum (Ny till äldre)
Releasedatum (Äldre till nytt)
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index b516de738..5ea5a7150 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -703,6 +703,5 @@
Çevrimdışı izlemek için kullanılabilir
Seekbar önizleme
Arama çubuğunda önizleme küçük resmini etkinleştir
- MPV YTDL
Henüz altyazı yüklenmedi
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index c5e234f0d..0b52780d2 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -406,7 +406,7 @@
Готово
Розширення
Додати репозиторій
- Назва репозиторію (Необов\'язково)
+ Назва репозиторію (необов\'язково)
URL-адреса репозиторію або короткий код
Плагін завантажено
Плагін завантажено
@@ -657,4 +657,8 @@
Попередній перегляд повзунка
Ввімкнути мініатюру попереднього перегляду на повзунку
Субтитри ще не завантажено
+ Підтвердити перед виходом
+ Показувати
+ Показувати діалог перед виходом із застосунку
+ Не показувати
\ No newline at end of file
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 5c4cd380e..4a63c3af4 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -400,8 +400,8 @@
Hoàn tất
Tiện ích mở rộng
Thêm kho lưu trữ
- Tên kho lưu trữ
- Đường dẫn kho lưu trữ
+ Tên kho lưu trữ (Tùy chọn)
+ Đường dẫn kho lưu trữ hoặc Mã ngắn
Đã tải plugin
Plugin đã xoá
Không tải được %s
@@ -670,4 +670,8 @@
Bật chế độ xem trước hình thu nhỏ trên seekbar
Xem trước Seekbar
Chưa tải phụ đề
+ Xác nhận trước khi thoát
+ Hiện hộp thoại trước khi thoát ứng dụng
+ Không hiển thị
+ Hiển thị
\ No newline at end of file
From d870978f3a70db4362b7179f0e9f909a865d2d57 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Mon, 14 Oct 2024 18:01:24 +0000
Subject: [PATCH 017/956] Removed crash reporting
---
.../lagradost/cloudstream3/AcraApplication.kt | 8 +--
.../ui/setup/SetupFragmentLayout.kt | 4 +-
.../main/res/layout/fragment_setup_layout.xml | 72 ++++++++++---------
app/src/main/res/xml/settings_updates.xml | 4 +-
4 files changed, 45 insertions(+), 43 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
index d6f978fe5..ba9401021 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
@@ -42,11 +42,11 @@ import kotlin.system.exitProcess
class CustomReportSender : ReportSender {
// Sends all your crashes to google forms
override fun send(context: Context, errorContent: CrashReportData) {
- println("Sending report")
+ /*println("Sending report")
val url =
- "https://docs.google.com/forms/d/e/1FAIpQLSfO4r353BJ79TTY_-t5KWSIJT2xfqcQWY81xjAA1-1N0U2eSg/formResponse"
+ "https://docs.google.com/forms/d/e/$id/formResponse"
val data = mapOf(
- "entry.1993829403" to errorContent.toJSON()
+ "entry.$entry" to errorContent.toJSON()
)
thread { // to not run it on main thread
@@ -62,7 +62,7 @@ class CustomReportSender : ReportSender {
normalSafeApiCall {
Toast.makeText(context, R.string.acra_report_toast, Toast.LENGTH_SHORT).show()
}
- }
+ }*/
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
index d8fa46e63..9de6038e3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
@@ -68,7 +68,7 @@ class SetupFragmentLayout : Fragment() {
.apply()
activity?.recreate()
}
- acraSwitch.setOnCheckedChangeListener { _, enableCrashReporting ->
+ /*acraSwitch.setOnCheckedChangeListener { _, enableCrashReporting ->
// Use same pref as in settings
settingsManager.edit().putBoolean(ACRA.PREF_DISABLE_ACRA, !enableCrashReporting)
.apply()
@@ -83,7 +83,7 @@ class SetupFragmentLayout : Fragment() {
crashReportingText.text =
getText(
if (enableCrashReporting) R.string.bug_report_settings_off else R.string.bug_report_settings_on
- )
+ )*/
nextBtt.setOnClickListener {
diff --git a/app/src/main/res/layout/fragment_setup_layout.xml b/app/src/main/res/layout/fragment_setup_layout.xml
index e14170e3f..a742c27b5 100644
--- a/app/src/main/res/layout/fragment_setup_layout.xml
+++ b/app/src/main/res/layout/fragment_setup_layout.xml
@@ -6,44 +6,46 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_margin="20dp"
+ android:orientation="horizontal">
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ -->
-
+ android:title="@string/pref_disable_acra" />-->
From f30319ff7cb3cdaa34207171c5f691f198e3430c Mon Sep 17 00:00:00 2001
From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com>
Date: Wed, 16 Oct 2024 19:42:40 +0000
Subject: [PATCH 018/956] Update OpenSubtitlesApi.kt (#1382)
---
.../cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt
index 37b956146..2e28f1945 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt
@@ -42,7 +42,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
/** Automatically adds required api headers */
private class OpenSubtitleInterceptor : Interceptor {
/** Required user agent! */
- private val userAgent = "Cloudstream3 v0.1"
+ private val userAgent = "Cloudstream3 v0.2"
override fun intercept(chain: Interceptor.Chain): Response {
return chain.proceed(
chain.request().newBuilder()
@@ -118,7 +118,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi
headers = mapOf(
"Content-Type" to "application/json",
),
- data = mapOf(
+ json = mapOf(
"username" to username,
"password" to password
),
From 217cf416ab267cf1054f11dd744a3c679c27f240 Mon Sep 17 00:00:00 2001
From: Elvis Tony
Date: Mon, 21 Oct 2024 15:58:16 +0100
Subject: [PATCH 019/956] Update CommonActivity.kt - Added Numpad 1,2 Channel
Up,Down for Navigating Episodes (#1387)
Added KeyEvent Support for Numpad 1 & Channel Down for Previous Episode and Numpad 2 & Channel Up for Next Episode.
Source: [Documentation](https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_CHANNEL_UP)
---
.../main/java/com/lagradost/cloudstream3/CommonActivity.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index 8498925c8..c541dde5b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -493,11 +493,11 @@ object CommonActivity {
PlayerEventType.SeekBack
}
- KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1, KeyEvent.KEYCODE_N -> {
+ KeyEvent.KEYCODE_MEDIA_NEXT, KeyEvent.KEYCODE_BUTTON_R1, KeyEvent.KEYCODE_N, KeyEvent.KEYCODE_NUMPAD_2, KeyEvent.KEYCODE_CHANNEL_UP -> {
PlayerEventType.NextEpisode
}
- KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_B -> {
+ KeyEvent.KEYCODE_MEDIA_PREVIOUS, KeyEvent.KEYCODE_BUTTON_L1, KeyEvent.KEYCODE_B, KeyEvent.KEYCODE_NUMPAD_1, KeyEvent.KEYCODE_CHANNEL_DOWN -> {
PlayerEventType.PrevEpisode
}
From 62b3c697d6f851693fa11d504068ae8f9da94bb3 Mon Sep 17 00:00:00 2001
From: RowdyRushya
Date: Fri, 25 Oct 2024 08:30:04 -0700
Subject: [PATCH 020/956] fix vidstreaming extractor (#1393)
---
.../lagradost/cloudstream3/extractors/helper/GogoHelper.kt | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
index 1766af6cf..35c23b2f7 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
@@ -107,8 +107,7 @@ object GogoHelper {
"$mainUrl/encrypt-ajax.php?$encryptRequestData",
headers = mapOf("X-Requested-With" to "XMLHttpRequest")
)
- val dataencrypted =
- jsonResponse.text.substringAfter("{\"data\":\"").substringBefore("\"}")
+ val dataencrypted = jsonResponse.parsedSafe()?.data ?: return@safeApiCall
val datadecrypted = cryptoHandler(dataencrypted, foundIv, foundDecryptKey, false)
val sources = AppUtils.parseJson(datadecrypted)
@@ -155,4 +154,8 @@ object GogoHelper {
@JsonProperty("type") val type: String?,
@JsonProperty("default") val default: String? = null
)
+
+ data class GogoJsonData(
+ @JsonProperty("data") val data: String? = null
+ )
}
\ No newline at end of file
From 83318b0b0f82cc4736fb2a39160f748d232f4110 Mon Sep 17 00:00:00 2001
From: IndusAryan <125901294+IndusAryan@users.noreply.github.com>
Date: Sat, 26 Oct 2024 21:49:39 +0530
Subject: [PATCH 021/956] feat(refactor): replace glide with coil image loading
library (fast & small) (#1368)
---
app/build.gradle.kts | 6 +-
.../lagradost/cloudstream3/AcraApplication.kt | 3 +
.../lagradost/cloudstream3/CommonActivity.kt | 2 +-
.../lagradost/cloudstream3/MainActivity.kt | 14 +-
.../cloudstream3/actions/OpenInAppAction.kt | 5 +-
.../cloudstream3/actions/VideoClickAction.kt | 2 +-
.../actions/temp/CopyClipboardAction.kt | 2 +-
.../cloudstream3/actions/temp/MpvKtPackage.kt | 2 +-
.../cloudstream3/actions/temp/MpvPackage.kt | 2 +-
.../actions/temp/PlayInBrowserAction.kt | 2 +-
.../actions/temp/ViewM3U8Action.kt | 2 +-
.../cloudstream3/actions/temp/VlcPackage.kt | 2 +-
.../actions/temp/WebVideoCastPackage.kt | 2 +-
.../actions/temp/fcast/FcastAction.kt | 2 +-
.../cloudstream3/plugins/PluginManager.kt | 4 +-
.../services/SubscriptionWorkManager.kt | 2 +-
.../cloudstream3/syncproviders/SyncApi.kt | 2 +-
.../syncproviders/providers/AniListApi.kt | 2 +-
.../syncproviders/providers/LocalList.kt | 2 +-
.../syncproviders/providers/MALApi.kt | 2 +-
.../syncproviders/providers/SimklApi.kt | 2 +-
.../cloudstream3/ui/account/AccountAdapter.kt | 14 +-
.../cloudstream3/ui/account/AccountHelper.kt | 7 +-
.../ui/download/DownloadAdapter.kt | 4 +-
.../cloudstream3/ui/home/HomeFragment.kt | 4 +-
.../cloudstream3/ui/home/HomeScrollAdapter.kt | 6 +-
.../ui/library/LibraryFragment.kt | 2 +-
.../ui/player/FullScreenPlayer.kt | 4 +-
.../player/source_priority/ProfilesAdapter.kt | 33 ++-
.../source_priority/QualityDataHelper.kt | 4 +-
.../source_priority/QualityProfileDialog.kt | 2 +-
.../source_priority/SourcePriorityDialog.kt | 2 +-
.../cloudstream3/ui/result/ActorAdaptor.kt | 8 +-
.../cloudstream3/ui/result/EpisodeAdapter.kt | 8 +-
.../ui/result/ResultFragmentPhone.kt | 22 +-
.../ui/result/ResultFragmentTv.kt | 34 ++--
.../ui/result/ResultViewModel2.kt | 15 +-
.../cloudstream3/ui/result/SelectAdaptor.kt | 2 +
.../ui/search/SearchResultBuilder.kt | 7 +-
.../ui/settings/AccountAdapter.kt | 25 +--
.../ui/settings/SettingsAccount.kt | 17 +-
.../ui/settings/SettingsFragment.kt | 22 +-
.../ui/settings/SettingsUpdates.kt | 2 +-
.../settings/extensions/ExtensionsFragment.kt | 2 +-
.../extensions/ExtensionsViewModel.kt | 4 +-
.../ui/settings/extensions/PluginAdapter.kt | 26 +--
.../extensions/PluginDetailsFragment.kt | 18 +-
.../settings/extensions/PluginsViewModel.kt | 2 +-
.../ui/settings/extensions/RepoAdapter.kt | 2 +-
.../lagradost/cloudstream3/utils/AniSkip.kt | 1 -
.../cloudstream3/utils/BackupUtils.kt | 1 -
.../cloudstream3/utils/DataStoreHelper.kt | 6 +-
.../lagradost/cloudstream3/utils/GlideApp.kt | 56 ------
.../cloudstream3/utils/ImageModuleCoil.kt | 162 +++++++++++++++
.../lagradost/cloudstream3/utils/ImageUtil.kt | 14 ++
.../utils/SingleSelectionHelper.kt | 4 +-
.../cloudstream3/utils/SnackbarHelper.kt | 1 -
.../result/UiText.kt => utils/TextUtil.kt} | 71 +------
.../lagradost/cloudstream3/utils/UIHelper.kt | 189 +-----------------
.../utils/VideoDownloadManager.kt | 34 +++-
60 files changed, 391 insertions(+), 510 deletions(-)
delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt
create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/ImageUtil.kt
rename app/src/main/java/com/lagradost/cloudstream3/{ui/result/UiText.kt => utils/TextUtil.kt} (63%)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 48a28e89b..f8b84772f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -173,11 +173,7 @@ dependencies {
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
-
- // Glide Module
- ksp("com.github.bumptech.glide:ksp:4.16.0")
- implementation("com.github.bumptech.glide:glide:4.16.0")
- implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
+ implementation("io.coil-kt:coil:2.7.0") // Coil Image Loading
// For KSP -> Official Annotation Processors are Not Yet Supported for KSP
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.2.0")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
index ba9401021..ac8877ed3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
@@ -22,6 +22,7 @@ import com.lagradost.cloudstream3.utils.DataStore.getKeys
import com.lagradost.cloudstream3.utils.DataStore.removeKey
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
import com.lagradost.cloudstream3.utils.DataStore.setKey
+import com.lagradost.cloudstream3.utils.ImageLoader
import kotlinx.coroutines.runBlocking
import org.acra.ACRA
import org.acra.ReportField
@@ -101,6 +102,8 @@ class AcraApplication : Application() {
override fun onCreate() {
super.onCreate()
+ ImageLoader.initializeCoilImageLoader(this)
+
ExceptionHandler(filesDir.resolve("last_error")) {
val intent = context!!.packageManager.getLaunchIntentForPackage(context!!.packageName)
startActivity(Intent.makeRestartActivityTask(intent!!.component))
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index c541dde5b..aea23d77d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -35,7 +35,7 @@ import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.databinding.ToastBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
-import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.UiText
import com.lagradost.cloudstream3.ui.settings.Globals.updateTv
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
import com.lagradost.cloudstream3.utils.Event
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 7ea399dac..60b743711 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -2,7 +2,6 @@ package com.lagradost.cloudstream3
import android.animation.ValueAnimator
import android.annotation.SuppressLint
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
@@ -10,7 +9,6 @@ import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Rect
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
@@ -114,10 +112,9 @@ import com.lagradost.cloudstream3.ui.result.LinearListLayout
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST
import com.lagradost.cloudstream3.ui.result.SyncViewModel
-import com.lagradost.cloudstream3.ui.result.setImage
-import com.lagradost.cloudstream3.ui.result.setText
-import com.lagradost.cloudstream3.ui.result.setTextHtml
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.setTextHtml
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.search.SearchFragment
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
@@ -174,6 +171,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.actions.temp.fcast.FcastManager
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.safefile.SafeFile
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -1372,7 +1370,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
resultviewPreviewMetaRating.setText(d.ratingText)
resultviewPreviewDescription.setTextHtml(d.plotText)
- resultviewPreviewPoster.setImage(
+ resultviewPreviewPoster.loadImage(
d.posterImage ?: d.posterBackgroundImage
)
@@ -1615,7 +1613,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
observe(homeViewModel.currentAccount) { currentAccount ->
if (currentAccount != null) {
- navProfilePic?.setImage(
+ navProfilePic?.loadImage(
currentAccount.image
)
navProfileRoot.isVisible = true
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
index 8abc5e665..cc64a6d39 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/OpenInAppAction.kt
@@ -8,14 +8,13 @@ import androidx.core.content.FileProvider
import androidx.core.net.toUri
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
-import com.lagradost.cloudstream3.MainActivity.Companion.activityResultLauncher
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.ui.result.ResultFragment
-import com.lagradost.cloudstream3.ui.result.UiText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UiText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.isAppInstalled
import com.lagradost.cloudstream3.utils.DataStoreHelper
import java.io.File
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
index 0d1e87a74..7e8b1c97b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/VideoClickAction.kt
@@ -25,7 +25,7 @@ import com.lagradost.cloudstream3.actions.temp.fcast.FcastAction
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.UiText
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
import com.lagradost.cloudstream3.utils.ExtractorLinkType
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
index 481917e5e..7e89d7c8c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/CopyClipboardAction.kt
@@ -4,7 +4,7 @@ import android.content.Context
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
class CopyClipboardAction: VideoClickAction() {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
index 6514de174..102f0ac8b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvKtPackage.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.actions.OpenInAppAction
import com.lagradost.cloudstream3.actions.updateDurationAndPosition
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLinkType
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
index 382f7d89a..68e619c92 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/MpvPackage.kt
@@ -11,7 +11,7 @@ import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.actions.updateDurationAndPosition
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLinkType
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
index 1558bfd6a..7c1b68c05 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/PlayInBrowserAction.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.ExtractorLinkType
class PlayInBrowserAction: VideoClickAction() {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
index b69562dd1..791566862 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/ViewM3U8Action.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
class ViewM3U8Action: VideoClickAction() {
override val name = txt(R.string.episode_action_play_in_format, "m3u8 player")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
index 18143c001..df4fcca81 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/VlcPackage.kt
@@ -12,7 +12,7 @@ import com.lagradost.cloudstream3.actions.makeTempM3U8Intent
import com.lagradost.cloudstream3.actions.updateDurationAndPosition
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.subtitles.SUBTITLE_AUTO_SELECT_KEY
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
index 4f44b3969..9f7eee7b8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/WebVideoCastPackage.kt
@@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.actions.OpenInAppAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.ExtractorLinkType
// https://www.webvideocaster.com/integrations
diff --git a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
index 458d1dc99..e3916df01 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/actions/temp/fcast/FcastAction.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.ui.result.LinkLoadingResult
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.ExtractorLinkType
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index 8535592d4..dc31b6ca1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -33,8 +33,8 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDE
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
import com.lagradost.cloudstream3.plugins.RepositoryManager.getRepoPlugins
-import com.lagradost.cloudstream3.ui.result.UiText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UiText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
import com.lagradost.cloudstream3.utils.AppContextUtils.getApiProviderLangSettings
diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
index 00c74dfff..aef92a6ab 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
@@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.plugins.PluginManager
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.AppContextUtils.getApiDubstatusSettings
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt
index dcb8bbead..9d43685c8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncApi.kt
@@ -3,7 +3,7 @@ package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.UiText
+import com.lagradost.cloudstream3.utils.UiText
import me.xdrop.fuzzywuzzy.FuzzySearch
import java.util.Date
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
index 6112c7dbe..68a4a9a54 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
@@ -15,7 +15,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.AppUtils.toJson
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
index 0d9a4d138..2b51f7efd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
index 08c186531..4836eca13 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt
@@ -18,7 +18,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.AppContextUtils.splitQuery
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
index 50517f9d1..92a181dd1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
@@ -29,7 +29,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.SyncWatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.lagradost.cloudstream3.utils.DataStoreHelper.toYear
import okhttp3.Interceptor
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
index de0b5c058..41ecbe7ff 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
@@ -6,17 +6,17 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
+import coil.transform.RoundedCornersTransformation
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.AccountListItemAddBinding
import com.lagradost.cloudstream3.databinding.AccountListItemBinding
import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog
-import com.lagradost.cloudstream3.ui.result.setImage
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.DataStoreHelper
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
class AccountAdapter(
private val accounts: List,
@@ -45,7 +45,7 @@ class AccountAdapter(
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
accountName.text = account.name
- accountImage.setImage(account.image)
+ accountImage.loadImage(account.image)
lockIcon.isVisible = account.lockPin != null
outline.isVisible = !isTv && isLastUsedAccount
@@ -87,11 +87,9 @@ class AccountAdapter(
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
accountName.text = account.name
- accountImage.setImage(
- account.image,
- fadeIn = false,
- radius = 10
- )
+ accountImage.loadImage(account.image) {
+ RoundedCornersTransformation(10f)
+ }
lockIcon.isVisible = account.lockPin != null
outline.isVisible = !isTv && isLastUsedAccount
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt
index d2aca862b..6abf0a348 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt
@@ -16,6 +16,7 @@ import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import coil.load
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.MainActivity
@@ -25,11 +26,11 @@ import com.lagradost.cloudstream3.databinding.AccountSelectLinearBinding
import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
-import com.lagradost.cloudstream3.ui.result.setImage
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod
@@ -90,12 +91,12 @@ object AccountHelper {
}
// Handle the profile picture and its interactions
- binding.accountImage.setImage(account.image)
+ binding.accountImage.loadImage(account.image)
binding.accountImage.setOnClickListener {
// Roll the image forwards once
currentEditAccount =
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % DataStoreHelper.profileImages.size)
- binding.accountImage.setImage(currentEditAccount.image)
+ binding.accountImage.loadImage(currentEditAccount.image)
}
// Handle applying changes
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt
index d211cb87c..a0e5cabc4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadAdapter.kt
@@ -18,7 +18,7 @@ import com.lagradost.cloudstream3.ui.download.button.DownloadStatusTell
import com.lagradost.cloudstream3.utils.AppContextUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
const val DOWNLOAD_ACTION_PLAY_FILE = 0
@@ -108,7 +108,7 @@ class DownloadAdapter(
}
downloadHeaderPoster.apply {
- setImage(data.poster)
+ loadImage(data.poster)
if (isMultiDeleteState) {
setOnClickListener {
toggleIsChecked(deleteCheckbox, data.id)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
index 2189c9e44..e8daa4eb4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt
@@ -22,7 +22,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.chip.Chip
-import com.lagradost.api.Log
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.CommonActivity.showToast
@@ -37,7 +36,7 @@ import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.search.*
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
@@ -58,7 +57,6 @@ import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import java.util.*
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt
index 29186e83a..4c4dd2d84 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt
@@ -13,7 +13,7 @@ import com.lagradost.cloudstream3.ui.ViewHolderState
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
class HomeScrollAdapter(
fragment: Fragment
@@ -47,7 +47,7 @@ class HomeScrollAdapter(
when (binding) {
is HomeScrollViewBinding -> {
- binding.homeScrollPreview.setImage(posterUrl)
+ binding.homeScrollPreview.loadImage(posterUrl)
binding.homeScrollPreviewTags.apply {
text = item.tags?.joinToString(" • ") ?: ""
isGone = item.tags.isNullOrEmpty()
@@ -57,7 +57,7 @@ class HomeScrollAdapter(
}
is HomeScrollViewTvBinding -> {
- binding.homeScrollPreview.setImage(posterUrl)
+ binding.homeScrollPreview.loadImage(posterUrl)
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
index 5b240693b..8e3abf058 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
@@ -46,7 +46,7 @@ import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD
import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
index 70f7e3686..f974436cb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt
@@ -53,8 +53,8 @@ import com.lagradost.cloudstream3.databinding.SubtitleOffsetBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
-import com.lagradost.cloudstream3.ui.result.setText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/ProfilesAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/ProfilesAdapter.kt
index 45f6aa660..886c8837b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/ProfilesAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/ProfilesAdapter.kt
@@ -8,13 +8,14 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.core.content.ContextCompat
+import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isVisible
+import androidx.palette.graphics.Palette
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.PlayerQualityProfileItemBinding
-import com.lagradost.cloudstream3.ui.result.UiImage
import com.lagradost.cloudstream3.utils.AppContextUtils
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
class ProfilesAdapter(
override val items: MutableList,
@@ -81,15 +82,25 @@ class ProfilesAdapter(
outline.isVisible = currentItem?.second?.id == item.id
- profileBg.setImage(UiImage.Drawable(art[index % art.size]), null, false) { palette ->
- val color = palette.getDarkVibrantColor(
- ContextCompat.getColor(
- itemView.context,
- R.color.dubColorBg
- )
- )
- wifiText.backgroundTintList = ColorStateList.valueOf(color)
- dataText.backgroundTintList = ColorStateList.valueOf(color)
+ profileBg.loadImage(art[index % art.size]) {
+ target { drawable ->
+ // Convert drawable to bitmap to extract palette colors
+ val bitmap = drawable.toBitmap()
+
+ Palette.from(bitmap).generate { palette ->
+ palette?.let {
+ val color = it.getDarkVibrantColor(
+ ContextCompat.getColor(
+ itemView.context,
+ R.color.dubColorBg
+ )
+ )
+
+ wifiText.backgroundTintList = ColorStateList.valueOf(color)
+ dataText.backgroundTintList = ColorStateList.valueOf(color)
+ }
+ }
+ }
}
val textStyle =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt
index 3267efd73..0922bdb5a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityDataHelper.kt
@@ -6,8 +6,8 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.debugAssert
-import com.lagradost.cloudstream3.ui.result.UiText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UiText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.DataStoreHelper.currentAccount
import com.lagradost.cloudstream3.utils.Qualities
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt
index 0537092c1..19e98138c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/QualityProfileDialog.kt
@@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.PlayerQualityProfileDialogBinding
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getProfileName
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper.getProfiles
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt
index bc6282af2..4c74ec80f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/source_priority/SourcePriorityDialog.kt
@@ -7,7 +7,7 @@ import androidx.annotation.StyleRes
import androidx.appcompat.app.AlertDialog
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.PlayerSelectSourcePriorityBinding
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ActorAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ActorAdaptor.kt
index 0ca326ddf..8ffe2e04b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ActorAdaptor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ActorAdaptor.kt
@@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.ActorData
import com.lagradost.cloudstream3.ActorRole
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.CastItemBinding
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
class ActorAdaptor(
private var nextFocusUpId: Int? = null,
@@ -102,7 +102,7 @@ class ActorAdaptor(
}
binding.apply {
- actorImage.setImage(mainImg)
+ actorImage.loadImage(mainImg)
actorName.text = actor.actor.name
actor.role?.let {
@@ -136,7 +136,9 @@ class ActorAdaptor(
voiceActorName.isVisible = false
} else {
voiceActorName.text = actor.voiceActor?.name
- voiceActorImageHolder.isVisible = voiceActorImage.setImage(vaImage)
+ if (!vaImage.isNullOrEmpty())
+ voiceActorImageHolder.isVisible = true
+ voiceActorImage.loadImage(vaImage)
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
index 2dd8e2ab4..51acd15a4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt
@@ -23,9 +23,11 @@ import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.html
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.txt
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
@@ -225,7 +227,7 @@ class EpisodeAdapter(
episodeProgress.isVisible = displayPos > 0L
}
- episodePoster.isVisible = episodePoster.setImage(card.poster) == true
+ episodePoster.loadImage(card.poster)
if (card.rating != null) {
episodeRating.text = episodeRating.context?.getString(R.string.rated_format)
@@ -260,7 +262,7 @@ class EpisodeAdapter(
episodePlayIcon.isVisible = false
episodeUpcomingIcon.isVisible = !episodePoster.isVisible
episodeDate.setText(
- txt(
+ com.lagradost.cloudstream3.utils.txt(
R.string.episode_upcoming_format,
secondsToReadable(
card.airDate.minus(unixTimeMS).div(1000).toInt(),
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index 97bc49eae..119bd57f0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -64,6 +64,7 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.openBrowser
import com.lagradost.cloudstream3.utils.AppContextUtils.updateHasTrailers
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.openBatteryOptimizationSettings
import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
@@ -75,8 +76,9 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
import com.lagradost.cloudstream3.utils.UIHelper.populateChips
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.setTextHtml
open class ResultFragmentPhone : FullScreenPlayer() {
private val gestureRegionsListener =
@@ -449,8 +451,8 @@ open class ResultFragmentPhone : FullScreenPlayer() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- showToast(txt(message, name), Toast.LENGTH_SHORT)
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ showToast(com.lagradost.cloudstream3.utils.txt(message, name), Toast.LENGTH_SHORT)
}
context?.let { openBatteryOptimizationSettings(it) }
}
@@ -465,8 +467,8 @@ open class ResultFragmentPhone : FullScreenPlayer() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- showToast(txt(message, name), Toast.LENGTH_SHORT)
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ showToast(com.lagradost.cloudstream3.utils.txt(message, name), Toast.LENGTH_SHORT)
}
}
mediaRouteButton.apply {
@@ -703,8 +705,12 @@ open class ResultFragmentPhone : FullScreenPlayer() {
resultCastText.setText(d.actorsText)
resultNextAiring.setText(d.nextAiringEpisode)
resultNextAiringTime.setText(d.nextAiringDate)
- resultPoster.setImage(d.posterImage)
- resultPosterBackground.setImage(d.posterBackgroundImage)
+ resultPoster.loadImage(d.posterImage, headers = d.posterHeaders) {
+ error(R.drawable.default_cover)
+ }
+ resultPosterBackground.loadImage(d.posterBackgroundImage, headers = d.posterHeaders) {
+ error(R.drawable.default_cover)
+ }
var isExpanded = false
resultDescription.apply {
@@ -776,7 +782,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
resultReloadConnectionOpenInBrowser.isVisible = data is Resource.Failure
resultTitle.setOnLongClickListener {
- clipboardHelper(txt(R.string.title), resultTitle.text)
+ clipboardHelper(com.lagradost.cloudstream3.utils.txt(R.string.title), resultTitle.text)
true
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
index 1878f0b8f..b92cc250c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
@@ -46,6 +46,7 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.html
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
import com.lagradost.cloudstream3.utils.AppContextUtils.loadCache
import com.lagradost.cloudstream3.utils.AppContextUtils.updateHasTrailers
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
import com.lagradost.cloudstream3.utils.UIHelper
@@ -53,7 +54,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.setTextHtml
class ResultFragmentTv : Fragment() {
private lateinit var viewModel: ResultViewModel2
@@ -150,7 +152,9 @@ class ResultFragmentTv : Fragment() {
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
// very dirty selection
resultRecommendationsFilterSelection.isVisible = apiNames.size > 1
- resultRecommendationsFilterSelection.update(apiNames.map { txt(it) to it })
+ resultRecommendationsFilterSelection.update(apiNames.map { com.lagradost.cloudstream3.utils.txt(
+ it
+ ) to it })
resultRecommendationsFilterSelection.select(apiNames.indexOf(matchAgainst))
} ?: run {
resultRecommendationsFilterSelection.isVisible = false
@@ -579,8 +583,12 @@ class ResultFragmentTv : Fragment() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(
+ com.lagradost.cloudstream3.utils.txt(
+ message,
+ name
+ ), Toast.LENGTH_SHORT)
}
}
}
@@ -622,8 +630,12 @@ class ResultFragmentTv : Fragment() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: txt(R.string.no_data).asStringNull(context) ?: ""
- CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ CommonActivity.showToast(
+ com.lagradost.cloudstream3.utils.txt(
+ message,
+ name
+ ), Toast.LENGTH_SHORT)
}
}
@@ -839,7 +851,7 @@ class ResultFragmentTv : Fragment() {
resultCastText.setText(d.actorsText)
resultNextAiring.setText(d.nextAiringEpisode)
resultNextAiringTime.setText(d.nextAiringDate)
- resultPoster.setImage(d.posterImage)
+ resultPoster.loadImage(d.posterImage)
var isExpanded = false
resultDescription.apply {
@@ -874,11 +886,9 @@ class ResultFragmentTv : Fragment() {
//Change poster crop area to 20% from Top
backgroundPoster.cropYCenterOffsetPct = 0.20F
- backgroundPoster.setImage(
- d.posterBackgroundImage ?: UiImage.Drawable(error),
- radius = 0,
- errorImageDrawable = error
- )
+ backgroundPoster.loadImage(
+ d.posterBackgroundImage
+ ) { error(error) }
comingSoon = d.comingSoon
resultTvComingSoon.isVisible = d.comingSoon
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index bf3b82a91..4cd3d4da5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -81,7 +81,6 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.setSubscribedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState
import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData
-import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import kotlinx.coroutines.*
import java.util.concurrent.TimeUnit
@@ -114,8 +113,8 @@ data class ResultData(
val title: String,
var syncData: Map,
- val posterImage: UiImage?,
- val posterBackgroundImage: UiImage?,
+ val posterImage: String?,
+ val posterBackgroundImage: String?,
val plotText: UiText,
val apiName: UiText,
val ratingText: UiText?,
@@ -131,6 +130,7 @@ data class ResultData(
val nextAiringDate: UiText?,
val nextAiringEpisode: UiText?,
val plotHeaderText: UiText,
+ val posterHeaders: Map? = null,
)
data class CheckDuplicateData(
@@ -215,12 +215,9 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
),
nextAiringDate = nextAiringDate,
nextAiringEpisode = nextAiringEpisode,
- posterImage = img(
- posterUrl, posterHeaders
- ) ?: img(R.drawable.default_cover),
- posterBackgroundImage = img(
- backgroundPosterUrl ?: posterUrl, posterHeaders
- ) ?: img(R.drawable.default_cover),
+ posterImage = posterUrl ?: backgroundPosterUrl,
+ posterHeaders = posterHeaders,
+ posterBackgroundImage = backgroundPosterUrl ?: posterUrl,
titleText = txt(name),
url = url,
tags = tags ?: emptyList(),
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt
index 8752e275c..ad5d89d18 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SelectAdaptor.kt
@@ -8,6 +8,8 @@ import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.databinding.ResultSelectionBinding
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
+import com.lagradost.cloudstream3.utils.UiText
+import com.lagradost.cloudstream3.utils.setText
typealias SelectData = Pair
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt
index 92575e58f..37e658bfc 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt
@@ -23,8 +23,8 @@ import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.getNameFull
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.fixVisual
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SubtitleHelper
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
object SearchResultBuilder {
private val showCache: MutableMap = mutableMapOf()
@@ -120,10 +120,7 @@ object SearchResultBuilder {
cardText?.text = card.name
cardText?.isVisible = showTitle
cardView.isVisible = true
-
- if (!cardView.setImage(card.posterUrl, card.posterHeaders, colorCallback = colorCallback)) {
- cardView.setImageResource(R.drawable.default_cover)
- }
+ cardView.loadImage(card.posterUrl) { error(R.drawable.default_cover) }
fun click(view: View?) {
clickCallback.invoke(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/AccountAdapter.kt
index d7bd69f11..ba53f96f1 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/AccountAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/AccountAdapter.kt
@@ -9,7 +9,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.AccountSingleBinding
import com.lagradost.cloudstream3.syncproviders.AuthAPI
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
class AccountClickCallback(val action: Int, val view: View, val card: AuthAPI.LoginInfo)
@@ -43,22 +43,23 @@ class AccountAdapter(
return cardList[position].accountIndex.toLong()
}
- class CardViewHolder(val binding: AccountSingleBinding, private val clickCallback: (AccountClickCallback) -> Unit) :
- RecyclerView.ViewHolder(binding.root) {
- // private val pfp: ImageView = itemView.findViewById(R.id.account_profile_picture)!!
- // private val accountName: TextView = itemView.findViewById(R.id.account_name)!!
+ class CardViewHolder(val binding: AccountSingleBinding?, private val clickCallback: (AccountClickCallback) -> Unit) :
+ RecyclerView.ViewHolder(binding?.root!!) {
@SuppressLint("StringFormatInvalid")
fun bind(card: AuthAPI.LoginInfo) {
// just in case name is null account index will show, should never happened
- binding.accountName.text = card.name ?: "%s %d".format(
- binding.accountName.context.getString(R.string.account),
- card.accountIndex
- )
- binding.accountProfilePicture.isVisible = binding.accountProfilePicture.setImage(card.profilePicture)
+ binding?.apply {
+ accountName.text = card.name ?: "%s %d".format(
+ binding.accountName.context.getString(R.string.account),
+ card.accountIndex
+ )
+ accountProfilePicture.isVisible = true
+ accountProfilePicture.loadImage(card.profilePicture)
- itemView.setOnClickListener {
- clickCallback.invoke(AccountClickCallback(0, itemView, card))
+ itemView.setOnClickListener {
+ clickCallback.invoke(AccountClickCallback(0, itemView, card))
+ }
}
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
index 15f8735fe..daeab0500 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
@@ -34,10 +34,6 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subDlAp
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.OAuth2API
-import com.lagradost.cloudstream3.ui.result.img
-import com.lagradost.cloudstream3.ui.result.setImage
-import com.lagradost.cloudstream3.ui.result.setText
-import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.TV
@@ -57,11 +53,13 @@ import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isAuthEnabled
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.promptInfo
import com.lagradost.cloudstream3.utils.BiometricAuthenticator.startBiometricAuthentication
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogText
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.txt
import qrcode.QRCode
class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
@@ -80,8 +78,9 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
.setView(binding.root)
val dialog = builder.show()
- binding.accountMainProfilePictureHolder.isVisible =
- binding.accountMainProfilePicture.setImage(info.profilePicture)
+ binding.accountMainProfilePictureHolder.isVisible = !info.profilePicture.isNullOrEmpty()
+ binding.accountMainProfilePicture.loadImage(info.profilePicture)
+
binding.accountLogout.setOnClickListener {
api.logOut()
@@ -198,9 +197,7 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
pinCodeData.verificationUrl
)
)
- deviceAuthQrcode.setImage(
- img(qrCodeImage)
- )
+ deviceAuthQrcode.loadImage(qrCodeImage)
}
val expirationMillis =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
index 339fe752d..ef35971ce 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt
@@ -20,17 +20,17 @@ import com.lagradost.cloudstream3.databinding.MainSettingsBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers
-import com.lagradost.cloudstream3.ui.home.HomeFragment
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.errorProfilePic
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.DataStoreHelper
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.UIHelper
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
import com.lagradost.cloudstream3.utils.UIHelper.navigate
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import java.io.File
import java.text.DateFormat
@@ -184,14 +184,14 @@ class SettingsFragment : Fragment() {
val login = syncApi.loginInfo()
val pic = login?.profilePicture ?: continue
- if (binding?.settingsProfilePic?.setImage(
- pic,
- errorImageDrawable = HomeFragment.errorProfilePic
- ) == true
- ) {
- binding?.settingsProfileText?.text = login.name
- return true // sync profile exists
+ binding?.settingsProfilePic?.let { imageView ->
+ imageView.loadImage(pic) {
+ crossfade(true) // Optional: for a fade-in animation
+ error(errorProfilePic) // Fallback to random error drawable
+ }
}
+ return true // sync profile exists
+
}
return false // not syncing
}
@@ -209,7 +209,7 @@ class SettingsFragment : Fragment() {
null
}
- binding?.settingsProfilePic?.setImage(currentAccount?.image)
+ binding?.settingsProfilePic?.loadImage(currentAccount?.image)
binding?.settingsProfileText?.text = currentAccount?.name
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
index 4c45480cd..46d32a2d2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
@@ -18,7 +18,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.services.BackupWorkManager
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
index 1b4876293..bd1e219d0 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt
@@ -28,7 +28,7 @@ import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
-import com.lagradost.cloudstream3.ui.result.setText
+import com.lagradost.cloudstream3.utils.setText
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt
index 866d167c1..ebe9fc888 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsViewModel.kt
@@ -12,8 +12,8 @@ import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginsOnline
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
-import com.lagradost.cloudstream3.ui.result.UiText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.UiText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
data class RepositoryData(
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
index d159539d6..ef74851ea 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt
@@ -5,7 +5,6 @@ import android.text.format.Formatter.formatShortFileSize
import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isGone
import androidx.core.view.isVisible
@@ -18,23 +17,23 @@ import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.VotingApi.getVotes
-import com.lagradost.cloudstream3.ui.result.setText
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.setText
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.html
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
+import org.junit.Assert
+import org.junit.Test
import java.text.DecimalFormat
import kotlin.math.floor
import kotlin.math.log10
import kotlin.math.pow
-import org.junit.Test
-import org.junit.Assert
data class PluginViewData(
val plugin: Plugin,
@@ -89,9 +88,7 @@ class PluginAdapter(
// Clear glide image because setImageResource doesn't override
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
if (holder is PluginViewHolder) {
- holder.binding.entryIcon.let { pluginIcon ->
- com.bumptech.glide.Glide.with(pluginIcon).clear(pluginIcon)
- }
+ holder.binding.entryIcon.loadImage(R.drawable.ic_github_logo)
}
super.onViewRecycled(holder)
}
@@ -200,20 +197,15 @@ class PluginAdapter(
binding.actionSettings.isVisible = false
}
- if (!binding.entryIcon.setImage(//itemView.entry_icon?.height ?:
+ binding.entryIcon.loadImage(
metadata.iconUrl?.replace(
"%size%",
"$iconSize"
)?.replace(
"%exact_size%",
"$iconSizeExact"
- ),
- null,
- errorImageDrawable = R.drawable.ic_baseline_extension_24
- )
- ) {
- binding.entryIcon.setImageResource(R.drawable.ic_baseline_extension_24)
- }
+ )
+ ) { error(R.drawable.ic_baseline_extension_24) }
binding.extVersion.isVisible = true
binding.extVersion.text = "v${metadata.version}"
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt
index 7d733be09..9f0ca7ae8 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt
@@ -19,10 +19,10 @@ import com.lagradost.cloudstream3.plugins.VotingApi.hasVoted
import com.lagradost.cloudstream3.plugins.VotingApi.vote
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
@@ -61,19 +61,9 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen
super.onViewCreated(view, savedInstanceState)
val metadata = data.plugin.second
binding?.apply {
- if (!pluginIcon.setImage(//plugin_icon?.height ?:
- metadata.iconUrl?.replace(
- "%size%",
- "$iconSize"
- )?.replace(
- "%exact_size%",
- "$iconSizeExact"
- ),
- null,
- errorImageDrawable = R.drawable.ic_baseline_extension_24
- )
- ) {
- pluginIcon.setImageResource(R.drawable.ic_baseline_extension_24)
+ pluginIcon.loadImage(metadata.iconUrl?.replace("%size%", "$iconSize")
+ ?.replace("%exact_size%", "$iconSizeExact")) {
+ error(R.drawable.ic_baseline_extension_24)
}
pluginName.text = metadata.name.removeSuffix("Provider")
pluginVersion.text = metadata.version.toString()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
index fd5422b2d..a6f914898 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt
@@ -19,7 +19,7 @@ import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.SitePlugin
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.Coroutines.runOnMainThread
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt
index faf6d38bf..f647e5533 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/RepoAdapter.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
-import com.lagradost.cloudstream3.ui.result.txt
+import com.lagradost.cloudstream3.utils.txt
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt
index f0c948a4a..820a01f9f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AniSkip.kt
@@ -7,7 +7,6 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.ResultEpisode
-import com.lagradost.cloudstream3.ui.result.txt
import java.lang.Long.min
object EpisodeSkip {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
index a44af8773..7584e9124 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
@@ -29,7 +29,6 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_U
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY
import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY
import com.lagradost.cloudstream3.syncproviders.providers.SubDlApi.Companion.SUBDL_SUBTITLES_USER_KEY
-import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
index 7ef7bc576..2e16fc8c7 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -22,7 +22,6 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting
-import com.lagradost.cloudstream3.ui.result.UiImage
import com.lagradost.cloudstream3.ui.result.VideoWatchState
import com.lagradost.cloudstream3.utils.AppContextUtils.filterProviderByPreferredMedia
import java.util.Calendar
@@ -133,10 +132,7 @@ object DataStoreHelper {
@JsonProperty("lockPin")
val lockPin: String? = null,
) {
- val image: UiImage
- get() = customImage?.let { UiImage.Image(it) } ?: UiImage.Drawable(
- profileImages.getOrNull(defaultImageIndex) ?: profileImages.first()
- )
+ val image get() = customImage?.let { UiImage.Image(it) } ?: profileImages.getOrNull(defaultImageIndex)?.let { UiImage.Drawable(it) } ?: UiImage.Drawable(profileImages.first())
}
const val TAG = "data_store_helper"
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt
deleted file mode 100644
index 38d3fe9ef..000000000
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/GlideApp.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package com.lagradost.cloudstream3.utils
-
-import android.annotation.SuppressLint
-import android.content.Context
-import com.bumptech.glide.Glide
-import com.bumptech.glide.GlideBuilder
-import com.bumptech.glide.Registry
-import com.bumptech.glide.annotation.GlideModule
-import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory
-import com.bumptech.glide.load.model.GlideUrl
-import com.bumptech.glide.module.AppGlideModule
-import com.bumptech.glide.request.RequestOptions
-import com.bumptech.glide.signature.ObjectKey
-import com.lagradost.cloudstream3.USER_AGENT
-import com.lagradost.cloudstream3.network.DdosGuardKiller
-import com.lagradost.cloudstream3.network.initClient
-import com.lagradost.nicehttp.Requests
-import java.io.InputStream
-
-@GlideModule
-class GlideModule : AppGlideModule() {
- @SuppressLint("CheckResult")
- override fun applyOptions(context: Context, builder: GlideBuilder) {
- super.applyOptions(context, builder)
- builder.apply {
- RequestOptions()
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .signature(ObjectKey(System.currentTimeMillis().toShort()))
- }.setDiskCache {
- // Possible to make this a setting in the future.
- val memoryCacheSizeBytes: Long = 1024 * 1024 * 100 // 100mb
- InternalCacheDiskCacheFactory(context, memoryCacheSizeBytes).build()
- }
- }
-
- // Needed for DOH
- // https://stackoverflow.com/a/61634041
- override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
- val client =
- Requests().apply {
- defaultHeaders = mapOf("user-agent" to USER_AGENT)
- }.initClient(context)
- .newBuilder()
- .addInterceptor(DdosGuardKiller(false))
- .build()
-
- registry.replace(
- GlideUrl::class.java,
- InputStream::class.java,
- OkHttpUrlLoader.Factory(client)
- )
- super.registerComponents(context, glide, registry)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt
new file mode 100644
index 000000000..49ece60b5
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageModuleCoil.kt
@@ -0,0 +1,162 @@
+package com.lagradost.cloudstream3.utils
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.net.Uri
+import android.util.Log
+import android.widget.ImageView
+import androidx.annotation.DrawableRes
+import coil.EventListener
+import coil.ImageLoader
+import coil.load
+import coil.memory.MemoryCache
+import coil.request.CachePolicy
+import coil.request.ErrorResult
+import coil.request.ImageRequest
+import coil.request.SuccessResult
+import com.lagradost.cloudstream3.USER_AGENT
+import com.lagradost.cloudstream3.network.DdosGuardKiller
+import okhttp3.HttpUrl
+import okhttp3.OkHttpClient
+import java.io.File
+import java.nio.ByteBuffer
+
+object ImageLoader {
+
+ private var instance: ImageLoader? = null
+
+ fun initializeCoilImageLoader(context: Context) {
+ if (instance == null) {
+ synchronized(this) {
+ instance = buildImageLoader(context.applicationContext)
+ }
+ }
+ }
+
+ private fun buildImageLoader(context: Context): ImageLoader {
+ val okHttpClient = OkHttpClient.Builder()
+ .addInterceptor(DdosGuardKiller(alwaysBypass = false))
+ .build()
+
+ return ImageLoader.Builder(context)
+ .crossfade(true)
+ .respectCacheHeaders(false)
+ /** !Only use default placeholders and errors, if not using this instance for local
+ * image buttons because when animating this will appear or in more cases **/
+ //.placeholder(R.drawable.logo)
+ //.error(R.drawable.logo)
+ .allowHardware(true)
+ .memoryCache {
+ MemoryCache.Builder(context)
+ .maxSizePercent(0.15) // Use 15% of the app's available memory for image caching
+ .build()
+ }
+ .diskCache {
+ coil.disk.DiskCache.Builder()
+ .maxSizeBytes(128 * 1024 * 1024) // 128 MB
+ .directory(context.cacheDir.resolve("cs3_image_cache"))
+ .maxSizePercent(0.02) // Use 2% of the device's storage space for disk caching
+ .build()
+ }
+ .diskCachePolicy(CachePolicy.ENABLED)
+ /** Pass interceptors with care, unnecessary passing tokens to servers
+ or image hosting services causes unauthorized exceptions **/
+ .okHttpClient(okHttpClient)
+ .eventListener(object : EventListener {
+ override fun onStart(request: ImageRequest) {
+ super.onStart(request)
+ Log.i("CoilImageLoader", "Loading Image ${request.data}")
+ }
+
+ override fun onSuccess(request: ImageRequest, result: SuccessResult) {
+ super.onSuccess(request, result)
+ Log.d("CoilImageLoader", "Image Loading successful")
+ }
+
+ override fun onError(request: ImageRequest, result: ErrorResult) {
+ super.onError(request, result)
+ Log.e("CoilImageLoadError", "Error loading image: ${result.throwable}")
+ }
+ })
+ .build()
+ }
+
+ fun ImageView.loadImage(
+ imageData: UiImage?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = when(imageData) {
+ is UiImage.Image -> loadImageInternal(imageData = imageData.url, headers = imageData.headers, builder = builder)
+ is UiImage.Bitmap -> loadImageInternal(imageData = imageData.bitmap, builder = builder)
+ is UiImage.Drawable -> loadImageInternal(imageData = imageData.resId, builder = builder)
+ null -> loadImageInternal(null, builder = builder)
+ }
+
+ fun ImageView.loadImage(
+ imageData: String?,
+ headers: Map? = null,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, headers = headers, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: Uri?,
+ headers: Map? = null,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData,headers = headers, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: HttpUrl?,
+ headers: Map? = null,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, headers = headers, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: File?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ fun ImageView.loadImage(
+ @DrawableRes imageData: Int?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: Drawable?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: Bitmap?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: ByteArray?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ fun ImageView.loadImage(
+ imageData: ByteBuffer?,
+ builder: ImageRequest.Builder.() -> Unit = {}
+ ) = loadImageInternal(imageData = imageData, builder = builder)
+
+ /** we use coil's built in loader with our global synchronized instance, this way we achieve
+ latest and complete functionality as well as stability **/
+ private fun ImageView.loadImageInternal(
+ imageData: Any?,
+ headers: Map? = null,
+ builder: ImageRequest.Builder.() -> Unit = {} // for placeholder, error & transformations
+ ) {
+ // clear image to avoid loading issues at fast scrolling (e.g, an image recycler)
+ this.load(null)
+
+ // Use Coil's built-in load method but with our custom module
+ this.load(imageData, instance ?: return) {
+ addHeader("User-Agent", USER_AGENT)
+ headers?.forEach { (key, value) ->
+ addHeader(key, value)
+ }
+ builder() // if passed
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ImageUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageUtil.kt
new file mode 100644
index 000000000..3f9d10de4
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ImageUtil.kt
@@ -0,0 +1,14 @@
+package com.lagradost.cloudstream3.utils
+
+import androidx.annotation.DrawableRes
+
+/// Type safe any image, because THIS IS NOT PYTHON
+sealed class UiImage {
+ data class Image(
+ val url: String,
+ val headers: Map? = null
+ ) : UiImage()
+
+ data class Drawable(@DrawableRes val resId: Int) : UiImage()
+ data class Bitmap(val bitmap: android.graphics.Bitmap) : UiImage()
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
index 70edf80c7..180131473 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SingleSelectionHelper.kt
@@ -24,9 +24,9 @@ import com.lagradost.cloudstream3.databinding.OptionsPopupTvBinding
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
+import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
object SingleSelectionHelper {
fun Activity?.showOptionSelectStringRes(
@@ -79,7 +79,7 @@ object SingleSelectionHelper {
binding.imageView.apply {
isGone = poster.isNullOrEmpty()
- setImage(poster)
+ loadImage(poster)
}
} else {
view?.popupMenuNoIconsAndNoStringRes(options.mapIndexed { index, s ->
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SnackbarHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SnackbarHelper.kt
index e6a77795e..b43b51c74 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/SnackbarHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SnackbarHelper.kt
@@ -8,7 +8,6 @@ import com.google.android.material.snackbar.Snackbar
import com.lagradost.api.Log
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.ui.result.UiText
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
object SnackbarHelper {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/UiText.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/TextUtil.kt
similarity index 63%
rename from app/src/main/java/com/lagradost/cloudstream3/ui/result/UiText.kt
rename to app/src/main/java/com/lagradost/cloudstream3/utils/TextUtil.kt
index 709199430..4f3a74737 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/UiText.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/TextUtil.kt
@@ -1,17 +1,13 @@
-package com.lagradost.cloudstream3.ui.result
+package com.lagradost.cloudstream3.utils
import android.content.Context
-import android.graphics.Bitmap
import android.util.Log
-import android.widget.ImageView
import android.widget.TextView
-import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.AppContextUtils.html
-import com.lagradost.cloudstream3.utils.UIHelper.setImage
sealed class UiText {
companion object {
@@ -77,71 +73,6 @@ sealed class UiText {
}
}
-sealed class UiImage {
- data class Image(
- val url: String,
- val headers: Map? = null,
- @DrawableRes val errorDrawable: Int? = null
- ) : UiImage()
-
- data class Drawable(@DrawableRes val resId: Int) : UiImage()
- data class Bitmap(val bitmap: android.graphics.Bitmap) : UiImage()
-}
-
-fun ImageView?.setImage(value: UiImage?, fadeIn: Boolean = true) {
- when (value) {
- is UiImage.Image -> setImageImage(value, fadeIn)
- is UiImage.Drawable -> setImageDrawable(value)
- is UiImage.Bitmap -> setImageBitmap(value)
- null -> {
- this?.isVisible = false
- }
- }
-}
-
-fun ImageView?.setImageImage(value: UiImage.Image, fadeIn: Boolean = true) {
- if (this == null) return
- this.isVisible = setImage(value.url, value.headers, value.errorDrawable, fadeIn)
-}
-
-fun ImageView?.setImageDrawable(value: UiImage.Drawable) {
- if (this == null) return
- this.isVisible = true
- this.setImage(UiImage.Drawable(value.resId))
-}
-
-fun ImageView?.setImageBitmap(value: UiImage.Bitmap) {
- if (this == null) return
- this.isVisible = true
- this.setImageBitmap(value.bitmap)
-}
-
-@JvmName("imgNull")
-fun img(
- url: String?,
- headers: Map? = null,
- @DrawableRes errorDrawable: Int? = null
-): UiImage? {
- if (url.isNullOrBlank()) return null
- return UiImage.Image(url, headers, errorDrawable)
-}
-
-fun img(
- url: String,
- headers: Map? = null,
- @DrawableRes errorDrawable: Int? = null
-): UiImage {
- return UiImage.Image(url, headers, errorDrawable)
-}
-
-fun img(@DrawableRes drawable: Int): UiImage {
- return UiImage.Drawable(drawable)
-}
-
-fun img(bitmap: Bitmap): UiImage {
- return UiImage.Bitmap(bitmap)
-}
-
fun txt(value: String): UiText {
return UiText.DynamicString(value)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
index ad1b6502d..e3164301f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt
@@ -13,23 +13,25 @@ import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Color
-import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.TransactionTooLargeException
import android.util.Log
-import android.view.*
+import android.view.Gravity
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
+import android.view.WindowInsets
+import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
-import android.widget.ImageView
import android.widget.ListAdapter
import android.widget.ListView
import android.widget.Toast.LENGTH_LONG
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
-import androidx.annotation.DrawableRes
import androidx.annotation.IdRes
import androidx.annotation.StyleRes
import androidx.appcompat.view.ContextThemeWrapper
@@ -40,7 +42,6 @@ import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
-import androidx.core.graphics.drawable.toBitmapOrNull
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.core.view.marginBottom
@@ -53,14 +54,6 @@ import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.NavHostFragment
import androidx.palette.graphics.Palette
import androidx.preference.PreferenceManager
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.DiskCacheStrategy
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.load.model.GlideUrl
-import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.RequestOptions.bitmapTransform
-import com.bumptech.glide.request.target.Target
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
@@ -70,13 +63,9 @@ import com.lagradost.cloudstream3.CommonActivity.activity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.ui.result.UiImage
-import com.lagradost.cloudstream3.ui.result.UiText
-import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.ui.settings.Globals
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
-import jp.wasabeef.glide.transformations.BlurTransformation
import kotlin.math.roundToInt
object UIHelper {
@@ -266,172 +255,6 @@ object UIHelper {
}
}
- /*inline fun bindViewBinding(
- inflater: LayoutInflater?,
- container: ViewGroup?,
- layout: Int
- ): Pair {
- return try {
- val localInflater = inflater ?: container?.context?.let { LayoutInflater.from(it) }
- ?: return null to txt(
- R.string.unable_to_inflate,
- "Requires inflater OR container"
- )//throw IllegalArgumentException("Requires inflater OR container"))
-
- //println("methods: ${T::class.java.methods.map { it.name }}")
- val bind = T::class.java.methods.first { it.name == "bind" }
- //val inflate = T::class.java.methods.first { it.name == "inflate" }
- val root = localInflater.inflate(layout, container, false)
- bind.invoke(null, root) as T to null
- } catch (t: Throwable) {
- logError(t)
- val message = txt(R.string.unable_to_inflate, t.message ?: "Primary constructor")
- // if the desired layout is not found then we inflate the casted layout
- /*try {
- val localInflater = inflater ?: container?.context?.let { LayoutInflater.from(it) }
- ?: return null to txt(
- R.string.unable_to_inflate,
- "Requires inflater OR container"
- )//throw IllegalArgumentException("Requires inflater OR container"))
-
- // we don't know what method to use as there are 2, but first *should* always be true
- return try {
- val inflate = T::class.java.methods.first { it.name == "inflate" }
- inflate.invoke(null, localInflater, container, false) as T
- } catch (_: Throwable) {
- val inflate = T::class.java.methods.last { it.name == "inflate" }
- inflate.invoke(null, localInflater, container, false) as T
- } to message
- } catch (t: Throwable) {
- logError(t)
- }*/
-
- null to message
- }
- }*/
-
- fun ImageView?.setImage(
- url: String?,
- headers: Map? = null,
- @DrawableRes
- errorImageDrawable: Int? = null,
- fadeIn: Boolean = true,
- radius: Int = 0,
- sample: Int = 3,
- colorCallback: ((Palette) -> Unit)? = null
- ): Boolean {
- if (url.isNullOrBlank()) return false
- this.setImage(
- UiImage.Image(url, headers, errorImageDrawable),
- errorImageDrawable,
- fadeIn,
- radius,
- sample,
- colorCallback
- )
- return true
- }
-
- fun ImageView?.setImage(
- uiImage: UiImage?,
- @DrawableRes
- errorImageDrawable: Int? = null,
- fadeIn: Boolean = true,
- radius: Int = 0,
- sample: Int = 3,
- colorCallback: ((Palette) -> Unit)? = null,
- ): Boolean {
- if (this == null || uiImage == null) return false
-
- val (glideImage, identifier) =
- (uiImage as? UiImage.Drawable)?.resId?.let {
- it to it.toString()
- } ?: (uiImage as? UiImage.Image)?.let { image ->
- GlideUrl(image.url) { image.headers ?: emptyMap() } to image.url
- } ?: return false
-
- return try {
- var builder = com.bumptech.glide.Glide.with(this)
- .load(glideImage)
- .skipMemoryCache(true)
- .diskCacheStrategy(DiskCacheStrategy.ALL).let { req ->
- if (fadeIn)
- req.transition(DrawableTransitionOptions.withCrossFade())
- else req
- }
-
- if (radius > 0) {
- builder = builder.apply(bitmapTransform(BlurTransformation(radius, sample)))
- }
-
- if (colorCallback != null) {
- builder = builder.listener(object : RequestListener {
-
- override fun onResourceReady(
- resource: Drawable,
- model: Any,
- target: Target?,
- dataSource: DataSource,
- isFirstResource: Boolean
- ): Boolean {
- resource.toBitmapOrNull()
- ?.let { bitmap ->
- createPaletteAsync(
- identifier,
- bitmap,
- colorCallback
- )
- }
- return false
- }
-
- override fun onLoadFailed(
- e: GlideException?,
- model: Any?,
- target: Target,
- isFirstResource: Boolean
- ): Boolean {
- return false
- }
- })
- }
-
- val res = if (errorImageDrawable != null)
- builder.error(errorImageDrawable).into(this)
- else
- builder.into(this)
- res.clearOnDetach()
-
- true
- } catch (e: Exception) {
- logError(e)
- false
- }
- }
-
- fun ImageView?.setImageBlur(
- url: String?,
- radius: Int,
- sample: Int = 3,
- headers: Map? = null
- ) {
- if (this == null || url.isNullOrBlank()) return
- try {
- val res = com.bumptech.glide.Glide.with(this)
- .load(GlideUrl(url) { headers ?: emptyMap() })
- .apply(bitmapTransform(BlurTransformation(radius, sample)))
- .transition(
- DrawableTransitionOptions.withCrossFade()
- )
- .skipMemoryCache(true)
- .diskCacheStrategy(DiskCacheStrategy.ALL)
- .into(this)
- res.clearOnDetach()
- } catch (e: Exception) {
- logError(e)
- }
- }
-
fun adjustAlpha(@ColorInt color: Int, factor: Float): Int {
val alpha = (Color.alpha(color) * factor).roundToInt()
val red = Color.red(color)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
index e0b78543a..bdaaeb246 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt
@@ -12,14 +12,16 @@ import android.util.Log
import androidx.annotation.DrawableRes
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import androidx.core.graphics.drawable.toBitmap
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import androidx.work.Data
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.model.GlideUrl
+import coil.ImageLoader
+import coil.request.ImageRequest
+import coil.request.SuccessResult
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
@@ -52,6 +54,7 @@ import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@@ -59,7 +62,6 @@ import java.io.Closeable
import java.io.File
import java.io.IOException
import java.io.OutputStream
-import java.lang.IllegalArgumentException
import java.util.*
const val DOWNLOAD_CHANNEL_ID = "cloudstream3.general"
@@ -231,15 +233,27 @@ object VideoDownloadManager {
return cachedBitmaps[url]
}
- val bitmap = Glide.with(this)
- .asBitmap()
- .load(GlideUrl(url) { headers ?: emptyMap() })
- .submit(720, 720)
- .get()
+ val imageLoader = ImageLoader(this)
- if (bitmap != null) {
- cachedBitmaps[url] = bitmap
+ val request = ImageRequest.Builder(this)
+ .data(url)
+ .apply {
+ headers?.forEach { (key, value) ->
+ addHeader(key, value)
+ }
+ }
+ .allowHardware(false) // Disable hardware bitmaps for compatibility
+ .build()
+
+ val bitmap = runBlocking {
+ val result = imageLoader.execute(request)
+ (result as? SuccessResult)?.drawable?.toBitmap()
}
+
+ bitmap?.let {
+ cachedBitmaps[url] = it
+ }
+
return bitmap
} catch (e: Exception) {
logError(e)
From 71943913754de1d9aa158b8d9420809ff7118db6 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Sat, 26 Oct 2024 17:24:53 +0000
Subject: [PATCH 022/956] fixed ugly M3 style on tv
---
.../res/color/color_primary_transparent.xml | 4 +
.../main/res/layout/fragment_result_tv.xml | 227 +++++++++---------
app/src/main/res/values/styles.xml | 6 +
3 files changed, 120 insertions(+), 117 deletions(-)
create mode 100644 app/src/main/res/color/color_primary_transparent.xml
diff --git a/app/src/main/res/color/color_primary_transparent.xml b/app/src/main/res/color/color_primary_transparent.xml
new file mode 100644
index 000000000..e6d1f8c9e
--- /dev/null
+++ b/app/src/main/res/color/color_primary_transparent.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index 893c19ff8..e9d090172 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -91,15 +91,13 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_gravity="center"
android:alpha="0.8"
android:scaleType="matrix"
- tools:src="@drawable/profile_bg_dark_blue" >
-
+ tools:src="@drawable/profile_bg_dark_blue" />
-
+ android:src="@drawable/background_shadow" />
+ android:layout_marginTop="225dp"
+ android:orientation="vertical">
+ tools:text="8 Episodes"
+ tools:visibility="visible" />
-
+
+
+
+
+
-
-
-
-
-
+ android:maxLines="1"
+ android:textColor="?attr/grayTextColor"
+ tools:ignore="RtlSymmetry"
+ tools:text="69m remaining" />
+
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_play_arrow_24"
+ app:iconPadding="0dp" />
+ tools:visibility="visible">
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_play_arrow_24"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_resume_arrow2"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_film_roll_24"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/outline_bookmark_add_24"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_favorite_border_24"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/baseline_notifications_none_24"
+ app:iconPadding="0dp" />
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/search_icon"
+ app:iconPadding="0dp" />
+ tools:visibility="visible">
-
+ android:tag="@string/tv_no_focus_tag"
+ app:icon="@drawable/ic_baseline_sort_24"
+ app:iconPadding="0dp" />
+ android:baselineAligned="false"
+ android:orientation="horizontal">
@@ -570,9 +563,9 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="wrap_content"
android:ellipsize="end"
android:fadingEdgeLength="30dp"
+ android:focusable="true"
android:foreground="@drawable/outline_drawable"
android:maxLines="7"
- android:focusable="true"
android:nextFocusUp="@id/result_play_parent"
android:nextFocusDown="@id/result_cast_items"
android:padding="5dp"
@@ -617,14 +610,14 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:textColor="?attr/textColor"
android:textSize="20sp"
android:textStyle="bold"
- tools:visibility="visible"
- android:visibility="gone" />
+ android:visibility="gone"
+ tools:visibility="visible" />
@@ -634,9 +627,9 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
+ android:descendantFocusability="afterDescendants"
android:nextFocusUp="@id/result_cast_items"
android:nextFocusDown="@id/result_recommendations_list"
- android:descendantFocusability="afterDescendants"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="2"
@@ -1042,19 +1035,19 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_height="wrap_content"
android:orientation="vertical">
-
+
-
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index db0066700..bfe435ef9 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -99,6 +99,12 @@
- 0dp
+
+
-
+
+
+
+
+
diff --git a/app/src/main/res/xml/settings_ui.xml b/app/src/main/res/xml/settings_ui.xml
index 9909478dc..08c0fba72 100644
--- a/app/src/main/res/xml/settings_ui.xml
+++ b/app/src/main/res/xml/settings_ui.xml
@@ -17,7 +17,7 @@
android:title="@string/app_layout" />
@@ -33,6 +33,18 @@
app:min="0"
app:seekBarIncrement="1"
app:showSeekBarValue="true" />
+
+
From fabef62ea6dfea0b7cb9dcbab925c6f46d0c93b0 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 26 Jun 2025 21:57:04 +0200
Subject: [PATCH 245/956] Fix(TV): Made backbutton hide episodes instead of
exit, Closes #1268
---
.../ui/result/ResultFragmentTv.kt | 62 ++++++++++++++-----
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
index ea055376e..a91f513e6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
@@ -46,6 +46,8 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.html
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
import com.lagradost.cloudstream3.utils.AppContextUtils.loadCache
import com.lagradost.cloudstream3.utils.AppContextUtils.updateHasTrailers
+import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.attachBackPressedCallback
+import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.detachBackPressedCallback
import com.lagradost.cloudstream3.utils.ImageLoader.loadImage
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
@@ -153,9 +155,11 @@ class ResultFragmentTv : Fragment() {
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
// very dirty selection
resultRecommendationsFilterSelection.isVisible = apiNames.size > 1
- resultRecommendationsFilterSelection.update(apiNames.map { com.lagradost.cloudstream3.utils.txt(
- it
- ) to it })
+ resultRecommendationsFilterSelection.update(apiNames.map {
+ com.lagradost.cloudstream3.utils.txt(
+ it
+ ) to it
+ })
resultRecommendationsFilterSelection.select(apiNames.indexOf(matchAgainst))
} ?: run {
resultRecommendationsFilterSelection.isVisible = false
@@ -224,8 +228,20 @@ class ResultFragmentTv : Fragment() {
}
}
+ override fun onDestroy() {
+ super.onDestroy()
+ activity?.detachBackPressedCallback(this@ResultFragmentTv.toString())
+ }
+
private fun toggleEpisodes(show: Boolean) {
binding?.apply {
+ if (show) {
+ activity?.attachBackPressedCallback(this@ResultFragmentTv.toString()) {
+ toggleEpisodes(false)
+ }
+ } else {
+ activity?.detachBackPressedCallback(this@ResultFragmentTv.toString())
+ }
episodesShadow.fade(show)
episodeHolderTv.fade(show)
if (episodesShadow.isRtl()) {
@@ -315,7 +331,7 @@ class ResultFragmentTv : Fragment() {
resultSubscribeButton to resultSubscribeText,
resultSearchButton to resultSearchText,
resultEpisodesShowButton to resultEpisodesShowText
- ).forEach { (button , text) ->
+ ).forEach { (button, text) ->
button.setOnFocusChangeListener { view, hasFocus ->
if (!hasFocus) {
@@ -325,13 +341,14 @@ class ResultFragmentTv : Fragment() {
}
text.isSelected = true
- if (button.tag == context?.getString(R.string.tv_no_focus_tag)){
- resultFinishLoading.scrollTo(0,0)
+ if (button.tag == context?.getString(R.string.tv_no_focus_tag)) {
+ resultFinishLoading.scrollTo(0, 0)
}
when (button.id) {
R.id.result_episodes_show_button -> {
toggleEpisodes(true)
}
+
else -> {
toggleEpisodes(false)
}
@@ -488,7 +505,12 @@ class ResultFragmentTv : Fragment() {
when {
resume.isMovie -> context?.getString(R.string.resume)
resume.result.season != null ->
- "${getString(R.string.season_short)}${resume.result.season}:${getString(R.string.episode_short)}${resume.result.episode}"
+ "${getString(R.string.season_short)}${resume.result.season}:${
+ getString(
+ R.string.episode_short
+ )
+ }${resume.result.episode}"
+
else -> "${getString(R.string.episode)} ${resume.result.episode}"
}
@@ -585,12 +607,14 @@ class ResultFragmentTv : Fragment() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data)
+ .asStringNull(context) ?: ""
CommonActivity.showToast(
com.lagradost.cloudstream3.utils.txt(
message,
name
- ), Toast.LENGTH_SHORT)
+ ), Toast.LENGTH_SHORT
+ )
}
}
}
@@ -632,12 +656,14 @@ class ResultFragmentTv : Fragment() {
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
- ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data).asStringNull(context) ?: ""
+ ?: com.lagradost.cloudstream3.utils.txt(R.string.no_data)
+ .asStringNull(context) ?: ""
CommonActivity.showToast(
com.lagradost.cloudstream3.utils.txt(
message,
name
- ), Toast.LENGTH_SHORT)
+ ), Toast.LENGTH_SHORT
+ )
}
}
@@ -653,7 +679,7 @@ class ResultFragmentTv : Fragment() {
}
observeNullable(viewModel.movie) { data ->
- if (data == null ) {
+ if (data == null) {
return@observeNullable
}
@@ -796,13 +822,19 @@ class ResultFragmentTv : Fragment() {
ep.getWatchProgress() >= NEXT_WATCH_EPISODE_PERCENTAGE.toFloat() / 100.0f || ep.videoWatchState == VideoWatchState.Watched
}
- val firstUnwatched = episodes.value.getOrElse(lastWatchedIndex + 1) { episodes.value.firstOrNull() }
+ val firstUnwatched =
+ episodes.value.getOrElse(lastWatchedIndex + 1) { episodes.value.firstOrNull() }
if (firstUnwatched != null) {
resultPlaySeriesText.text =
when {
firstUnwatched.season != null ->
- "${getString(R.string.season_short)}${firstUnwatched.season}:${getString(R.string.episode_short)}${firstUnwatched.episode}"
+ "${getString(R.string.season_short)}${firstUnwatched.season}:${
+ getString(
+ R.string.episode_short
+ )
+ }${firstUnwatched.episode}"
+
else -> "${getString(R.string.episode)} ${firstUnwatched.episode}"
}
resultPlaySeriesButton.setOnClickListener {
@@ -887,7 +919,7 @@ class ResultFragmentTv : Fragment() {
).random()
//Change poster crop area to 20% from Top
backgroundPoster.cropYCenterOffsetPct = 0.20F
-
+
backgroundPoster.loadImage(d.posterBackgroundImage) {
error { getImageFromDrawable(context ?: return@error null, error) }
}
From d2e64f29c70f76d3a6694cb84e481fca1f9e78b2 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 26 Jun 2025 23:34:58 +0200
Subject: [PATCH 246/956] Chore: Replaced all instances of outdated mvvm apis
---
.../lagradost/cloudstream3/AcraApplication.kt | 8 ++++----
.../com/lagradost/cloudstream3/MainActivity.kt | 12 ++++++------
.../cloudstream3/network/CloudflareKiller.kt | 6 +++---
.../cloudstream3/network/RequestsHelper.kt | 4 ++--
.../cloudstream3/plugins/PluginManager.kt | 8 ++++----
.../cloudstream3/plugins/RepositoryManager.kt | 18 +++++++++---------
.../cloudstream3/syncproviders/SyncRepo.kt | 6 +++---
.../syncproviders/providers/AniListApi.kt | 6 +++---
.../syncproviders/providers/SimklApi.kt | 4 ++--
.../ui/download/DownloadFragment.kt | 6 +++---
.../ui/player/AbstractPlayerFragment.kt | 4 ++--
.../cloudstream3/ui/player/CS3IPlayer.kt | 4 ++--
.../ui/player/DownloadedPlayerActivity.kt | 4 ++--
.../cloudstream3/ui/player/GeneratorPlayer.kt | 14 +++++++-------
.../ui/player/PlayerGeneratorViewModel.kt | 18 +++++++++---------
.../cloudstream3/ui/player/PlayerPipHelper.kt | 4 ++--
.../ui/result/ResultFragmentPhone.kt | 6 +++---
.../cloudstream3/ui/result/ResultViewModel2.kt | 4 ++--
.../ui/settings/SettingsGeneral.kt | 4 ++--
.../ui/settings/SettingsUpdates.kt | 4 ++--
.../ui/settings/testing/TestFragment.kt | 4 ++--
.../ui/setup/SetupFragmentExtensions.kt | 6 +++---
.../ui/setup/SetupFragmentLanguage.kt | 8 ++++----
.../ui/setup/SetupFragmentLayout.kt | 6 +++---
.../ui/setup/SetupFragmentMedia.kt | 6 +++---
.../ui/setup/SetupFragmentProviderLanguage.kt | 6 +++---
.../cloudstream3/utils/AppContextUtils.kt | 8 ++++----
.../network/WebViewResolver.android.kt | 4 ++--
.../com/lagradost/cloudstream3/MainAPI.kt | 10 +++++-----
.../cloudstream3/extractors/Pelisplus.kt | 4 ++--
.../extractors/helper/GogoHelper.kt | 4 ++--
.../metaproviders/SyncRedirector.kt | 4 ++--
.../cloudstream3/utils/ExtractorApi.kt | 2 +-
33 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
index 003d79a77..9f493fbbc 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt
@@ -11,8 +11,8 @@ import androidx.fragment.app.FragmentActivity
import coil3.PlatformContext
import coil3.SingletonImageLoader
import com.lagradost.api.setContext
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
@@ -54,7 +54,7 @@ class CustomReportSender : ReportSender {
thread { // to not run it on main thread
runBlocking {
- suspendSafeApiCall {
+ safeAsync {
app.post(url, data = data)
//println("Report response: $post")
}
@@ -62,7 +62,7 @@ class CustomReportSender : ReportSender {
}
runOnMainThread { // to run it on main looper
- normalSafeApiCall {
+ safe {
Toast.makeText(context, R.string.acra_report_toast, Toast.LENGTH_SHORT).show()
}
}*/
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index dedb7fadd..682e69e34 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -81,7 +81,7 @@ import com.lagradost.cloudstream3.databinding.ActivityMainTvBinding
import com.lagradost.cloudstream3.databinding.BottomResultviewPreviewBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.network.initClient
@@ -266,7 +266,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
// TODO MUCH BETTER HANDLING
// Invalid URIs can crash
- fun safeURI(uri: String) = normalSafeApiCall { URI(uri) }
+ fun safeURI(uri: String) = safe { URI(uri) }
if (str != null && this != null) {
if (str.startsWith("https://cs.repo")) {
@@ -1138,15 +1138,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
updateTv()
// backup when we update the app, I don't trust myself to not boot lock users, might want to make this a setting?
- normalSafeApiCall {
+ safe {
val appVer = BuildConfig.VERSION_NAME
val lastAppAutoBackup: String = getKey("VERSION_NAME") ?: ""
if (appVer != lastAppAutoBackup) {
setKey("VERSION_NAME", BuildConfig.VERSION_NAME)
- normalSafeApiCall {
+ safe {
backup(this)
}
- normalSafeApiCall {
+ safe {
// Recompile oat on new version
PluginManager.deleteAllOatFiles(this)
}
@@ -1251,7 +1251,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
ioSafe { SafeFile.check(this@MainActivity) }
if (PluginManager.checkSafeModeFile()) {
- normalSafeApiCall {
+ safe {
showToast(R.string.safe_mode_file, Toast.LENGTH_LONG)
}
} else if (lastError == null) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt
index 85a9db5db..9efa88a37 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt
@@ -5,7 +5,7 @@ import android.webkit.CookieManager
import androidx.annotation.AnyThread
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.debugWarning
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.cookies
import kotlinx.coroutines.runBlocking
@@ -32,7 +32,7 @@ class CloudflareKiller : Interceptor {
init {
// Needs to clear cookies between sessions to generate new cookies.
- normalSafeApiCall {
+ safe {
// This can throw an exception on unsupported devices :(
CookieManager.getInstance().removeAllCookies(null)
}
@@ -77,7 +77,7 @@ class CloudflareKiller : Interceptor {
}
private fun getWebViewCookie(url: String): String? {
- return normalSafeApiCall {
+ return safe {
CookieManager.getInstance()?.getCookie(url)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt
index 1565d92cf..ec486d61d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt
@@ -4,7 +4,7 @@ import android.content.Context
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.USER_AGENT
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ignoreAllSSLErrors
import okhttp3.Cache
@@ -20,7 +20,7 @@ fun Requests.initClient(context: Context) {
}
fun buildDefaultClient(context: Context): OkHttpClient {
- normalSafeApiCall { Security.insertProviderAt(Conscrypt.newProvider(), 1) }
+ safe { Security.insertProviderAt(Conscrypt.newProvider(), 1) }
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
val dns = settingsManager.getInt(context.getString(R.string.dns_pref), 0)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index 524d571d2..6e59696ae 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -38,7 +38,7 @@ import com.lagradost.cloudstream3.actions.VideoClickActionHolder
import com.lagradost.cloudstream3.apmap
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.plugins.RepositoryManager.ONLINE_PLUGINS_FOLDER
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.plugins.RepositoryManager.downloadPluginToFile
@@ -144,7 +144,7 @@ object PluginManager {
!it.filePath.contains(repositoryPath)
}
val file = File(repositoryPath)
- normalSafeApiCall {
+ safe {
if (file.exists()) file.deleteRecursively()
}
setKey(PLUGINS_KEY, plugins)
@@ -577,9 +577,9 @@ object PluginManager {
* @return true if safe mode file is present
**/
fun checkSafeModeFile(): Boolean {
- return normalSafeApiCall {
+ return safe {
val folder = File(CLOUD_STREAM_FOLDER)
- if (!folder.exists()) return@normalSafeApiCall false
+ if (!folder.exists()) return@safe false
val files = folder.listFiles { _, name ->
name.equals("safe", ignoreCase = true)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt
index c6ec9df7f..d92e81acd 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt
@@ -9,8 +9,8 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.plugins.PluginManager.getPluginSanitizedFileName
import com.lagradost.cloudstream3.plugins.PluginManager.unloadPlugin
import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
@@ -94,12 +94,12 @@ object RepositoryManager {
else fixedUrl
}
} else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) {
- suspendSafeApiCall {
+ safeAsync {
app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 ->
it2.headers["Location"]?.let { url ->
- if (url.startsWith("https://cutt.ly/404")) return@suspendSafeApiCall null
- if (url.removeSuffix("/") == "https://cutt.ly") return@suspendSafeApiCall null
- return@suspendSafeApiCall url
+ if (url.startsWith("https://cutt.ly/404")) return@safeAsync null
+ if (url.removeSuffix("/") == "https://cutt.ly") return@safeAsync null
+ return@safeAsync url
}
}
}
@@ -107,7 +107,7 @@ object RepositoryManager {
}
suspend fun parseRepository(url: String): Repository? {
- return suspendSafeApiCall {
+ return safeAsync {
// Take manifestVersion and such into account later
app.get(convertRawGitUrl(url)).parsedSafe()
}
@@ -142,7 +142,7 @@ object RepositoryManager {
pluginUrl: String,
file: File
): File? {
- return suspendSafeApiCall {
+ return safeAsync {
file.mkdirs()
// Overwrite if exists
@@ -191,7 +191,7 @@ object RepositoryManager {
// Unload all plugins, not using deletePlugin since we
// delete all data and files in deleteRepositoryData
- normalSafeApiCall {
+ safe {
file.listFiles { plugin: File ->
unloadPlugin(plugin.absolutePath)
false
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt
index 9363cb6fb..df88eeb71 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt
@@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.syncproviders
import com.lagradost.cloudstream3.ErrorLoadingException
import com.lagradost.cloudstream3.mvvm.Resource
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.safeApiCall
class SyncRepo(private val repo: SyncAPI) {
@@ -39,10 +39,10 @@ class SyncRepo(private val repo: SyncAPI) {
}
fun hasAccount(): Boolean {
- return normalSafeApiCall { repo.loginInfo() != null } ?: false
+ return safe { repo.loginInfo() != null } ?: false
}
- fun getIdFromUrl(url: String): String? = normalSafeApiCall {
+ fun getIdFromUrl(url: String): String? = safe {
repo.getIdFromUrl(url)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
index 370ad2304..f8e824095 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt
@@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.SyncAPI
@@ -530,13 +530,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI {
}
private suspend fun postApi(q: String, cache: Boolean = false): String? {
- return suspendSafeApiCall {
+ return safeAsync {
if (!checkToken()) {
app.post(
"https://graphql.anilist.co/",
headers = mapOf(
"Authorization" to "Bearer " + (getAuth()
- ?: return@suspendSafeApiCall null),
+ ?: return@safeAsync null),
if (cache) "Cache-Control" to "max-stale=$MAX_STALE" else "Cache-Control" to "no-cache"
),
cacheTime = 0,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
index 92a181dd1..519fb4c3a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SimklApi.kt
@@ -21,7 +21,7 @@ import com.lagradost.cloudstream3.mapper
import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.AuthAPI
import com.lagradost.cloudstream3.syncproviders.OAuth2API
@@ -760,7 +760,7 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
private suspend fun getUser(): SettingsResponse.User? {
- return suspendSafeApiCall {
+ return safeAsync {
app.post("$mainUrl/users/settings", interceptor = interceptor)
.parsedSafe()?.user
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
index d8c2fb2e1..fff70e975 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt
@@ -29,7 +29,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentDownloadsBinding
import com.lagradost.cloudstream3.databinding.StreamInputBinding
import com.lagradost.cloudstream3.isEpisodeBased
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
import com.lagradost.cloudstream3.ui.player.BasicLink
@@ -309,7 +309,7 @@ class DownloadFragment : Fragment() {
.setType("video/*")
.addCategory(Intent.CATEGORY_OPENABLE)
.addFlags(FLAG_GRANT_READ_URI_PERMISSION) // Request temporary access
- normalSafeApiCall {
+ safe {
videoResultLauncher.launch(
Intent.createChooser(
intent,
@@ -367,7 +367,7 @@ class DownloadFragment : Fragment() {
}
private fun activateSwitchOnHls(text: String?, binding: StreamInputBinding) {
- binding.hlsSwitch.isChecked = normalSafeApiCall {
+ binding.hlsSwitch.isChecked = safe {
URI(text).path?.substringAfterLast(".")?.contains("m3u")
} == true
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt
index fc4e38a0e..7dcf04c07 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/AbstractPlayerFragment.kt
@@ -44,7 +44,7 @@ import com.lagradost.cloudstream3.CommonActivity.screenWidth
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.unixTimeMs
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
@@ -243,7 +243,7 @@ abstract class AbstractPlayerFragment(
exitedPipMode()
pipReceiver?.let {
// Prevents java.lang.IllegalArgumentException: Receiver not registered
- normalSafeApiCall {
+ safe {
activity?.unregisterReceiver(it)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
index 9d5539355..e311f7b0d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt
@@ -72,7 +72,7 @@ import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle
@@ -1166,7 +1166,7 @@ class CS3IPlayer : IPlayer {
exoPlayer?.addListener(object : Player.Listener {
override fun onTracksChanged(tracks: Tracks) {
- normalSafeApiCall {
+ safe {
val textTracks = tracks.groups.filter { it.type == TRACK_TYPE_TEXT }
playerSelectedSubtitleTracks =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
index a50d1f6ca..88787c76b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt
@@ -7,7 +7,7 @@ import android.view.KeyEvent
import androidx.appcompat.app.AppCompatActivity
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.ui.player.OfflinePlaybackHelper.playLink
import com.lagradost.cloudstream3.ui.player.OfflinePlaybackHelper.playUri
import com.lagradost.cloudstream3.utils.BackPressedCallbackHelper.attachBackPressedCallback
@@ -36,7 +36,7 @@ class DownloadedPlayerActivity : AppCompatActivity() {
val data = intent.data
if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_OPEN_DOCUMENT || intent?.action == Intent.ACTION_VIEW) {
- val extraText = normalSafeApiCall { // I dont trust android
+ val extraText = safe { // I dont trust android
intent.getStringExtra(Intent.EXTRA_TEXT)
}
val cd = intent.clipData
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
index e4a8b2088..0eb28e5b5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
@@ -66,7 +66,7 @@ import com.lagradost.cloudstream3.isLiveStream
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.subtitles.AbstractSubApi
@@ -910,10 +910,10 @@ class GeneratorPlayer : FullScreenPlayer() {
// Open file picker
private val subsPathPicker =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
- normalSafeApiCall {
+ safe {
// It lies, it can be null if file manager quits.
- if (uri == null) return@normalSafeApiCall
- val ctx = context ?: AcraApplication.context ?: return@normalSafeApiCall
+ if (uri == null) return@safe
+ val ctx = context ?: AcraApplication.context ?: return@safe
// RW perms for the path
ctx.contentResolver.takePersistableUriPermission(
uri,
@@ -1060,7 +1060,7 @@ class GeneratorPlayer : FullScreenPlayer() {
var shouldDismiss = true
binding.subtitleSettingsBtt.setOnClickListener {
- normalSafeApiCall {
+ safe {
SubtitlesFragment().show(this.parentFragmentManager, "SubtitleSettings")
}
}
@@ -1732,7 +1732,7 @@ class GeneratorPlayer : FullScreenPlayer() {
private fun autoSelectSubtitles() {
//Log.i(TAG, "autoSelectSubtitles")
- normalSafeApiCall {
+ safe {
if (!autoSelectFromSettings()) {
autoSelectFromDownloads()
}
@@ -2024,7 +2024,7 @@ class GeneratorPlayer : FullScreenPlayer() {
val wasGone = binding?.overlayLoadingSkipButton?.isGone == true
binding?.overlayLoadingSkipButton?.isVisible = turnVisible
- normalSafeApiCall {
+ safe {
if (currentLinks.any { link ->
getLinkPriority(currentQualityProfile, link) >=
QualityDataHelper.AUTO_SKIP_PRIORITY
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
index cb170d125..023cedd8a 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.launchSafe
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.ui.result.ResultEpisode
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
@@ -114,20 +114,20 @@ class PlayerGeneratorViewModel : ViewModel() {
}
fun getLoadResponse(): LoadResponse? {
- return normalSafeApiCall { (generator as? RepoLinkGenerator?)?.page }
+ return safe { (generator as? RepoLinkGenerator?)?.page }
}
fun getMeta(): Any? {
- return normalSafeApiCall { generator?.getCurrent() }
+ return safe { generator?.getCurrent() }
}
fun getAllMeta(): List? {
- return normalSafeApiCall { generator?.getAll() }
+ return safe { generator?.getAll() }
}
fun getNextMeta(): Any? {
- return normalSafeApiCall {
- if (generator?.hasNext() == false) return@normalSafeApiCall null
+ return safe {
+ if (generator?.hasNext() == false) return@safe null
generator?.getCurrent(offset = 1)
}
}
@@ -204,8 +204,8 @@ class PlayerGeneratorViewModel : ViewModel() {
synchronized(currentLinks) {
currentLinks.add(it)
// Clone to prevent ConcurrentModificationException
- normalSafeApiCall {
- // Extra normalSafeApiCall since .toSet() iterates.
+ safe {
+ // Extra safe since .toSet() iterates.
_currentLinks.postValue(currentLinks.toSet())
}
}
@@ -213,7 +213,7 @@ class PlayerGeneratorViewModel : ViewModel() {
subtitleCallback = {
synchronized(extraSubtitles) {
currentSubs.add(it)
- normalSafeApiCall {
+ safe {
_currentSubs.postValue(currentSubs + extraSubtitles)
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt
index 972f8cf3d..cc99b585f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerPipHelper.kt
@@ -11,7 +11,7 @@ import android.util.Rational
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import com.lagradost.cloudstream3.R
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import kotlin.math.roundToInt
class PlayerPipHelper {
@@ -102,7 +102,7 @@ class PlayerPipHelper {
Rational((it * ratioAccuracy).roundToInt(), ratioAccuracy)
}
- normalSafeApiCall {
+ safe {
activity.setPictureInPictureParams(
PictureInPictureParams.Builder()
.apply {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index c873f2697..bd830aa2b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -44,7 +44,7 @@ import com.lagradost.cloudstream3.databinding.ResultRecommendationsBinding
import com.lagradost.cloudstream3.databinding.ResultSyncBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
@@ -891,7 +891,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
fun setSyncMaxEpisodes(totalEpisodes: Int?) {
syncBinding?.resultSyncEpisodes?.max = (totalEpisodes ?: 0) * 1000
- normalSafeApiCall {
+ safe {
val ctx = syncBinding?.resultSyncEpisodes?.context
syncBinding?.resultSyncMaxEpisodes?.text =
totalEpisodes?.let { episodes ->
@@ -961,7 +961,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
}
resultSyncCurrentEpisodes.text =
Editable.Factory.getInstance()?.newEditable(watchedEpisodes.toString())
- normalSafeApiCall { // format might fail
+ safe { // format might fail
context?.getString(R.string.sync_score_format)?.format(d.score ?: 0)
?.let {
resultSyncScoreText.text = it
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index fdd2f11d5..0e7e1fdb6 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -1212,7 +1212,7 @@ class ResultViewModel2 : ViewModel() {
}
private fun getImdbIdFromSyncData(syncData: Map?): String? {
- return normalSafeApiCall {
+ return safe {
val imdbId = readIdFromString(
syncData?.get(AccountManager.simklApi.idPrefix)
)[SimklSyncServices.Imdb]
@@ -1221,7 +1221,7 @@ class ResultViewModel2 : ViewModel() {
}
private fun getTMDbIdFromSyncData(syncData: Map?): String? {
- return normalSafeApiCall {
+ return safe {
val tmdbId = readIdFromString(
syncData?.get(AccountManager.simklApi.idPrefix)
)[SimklSyncServices.Tmdb]
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
index 74910ecbc..e82481ffa 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt
@@ -22,7 +22,7 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.databinding.AddRemoveSitesBinding
import com.lagradost.cloudstream3.databinding.AddSiteInputBinding
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.TV
@@ -319,7 +319,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
}
fun getDownloadDirs(): List {
- return normalSafeApiCall {
+ return safe {
context?.let { ctx ->
val defaultDir = VideoDownloadManager.getDefaultDir(ctx)?.filePath()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
index 4c69cb38f..bacca67ec 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt
@@ -16,7 +16,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.databinding.LogcatBinding
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.services.BackupWorkManager
@@ -264,7 +264,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
}
private fun getBackupDirsForDisplay(): List {
- return normalSafeApiCall {
+ return safe {
context?.let { ctx ->
val defaultDir = BackupUtils.getDefaultBackupDir(ctx)?.filePath()
val first = listOf(defaultDir)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
index 7878afaac..921ac0674 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/testing/TestFragment.kt
@@ -8,7 +8,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentTestingBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
import com.lagradost.cloudstream3.ui.settings.Globals.TV
@@ -47,7 +47,7 @@ class TestFragment : Fragment() {
}
observeNullable(testViewModel.providerResults) {
- normalSafeApiCall {
+ safe {
val newItems = it.sortedBy { api -> api.first.name }
(providerTestRecyclerView.adapter as? TestResultAdapter)?.updateList(
newItems
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
index 4369b22f9..0dccd5cc4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentExtensions.kt
@@ -11,7 +11,7 @@ import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupExtensionsBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel
@@ -93,8 +93,8 @@ class SetupFragmentExtensions : Fragment() {
// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this)
// }
- normalSafeApiCall {
- // val ctx = context ?: return@normalSafeApiCall
+ safe {
+ // val ctx = context ?: return@safe
setRepositories()
binding?.apply {
if (!isSetup) {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
index fc7fe093f..f57d4f159 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLanguage.kt
@@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupLanguageBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
@@ -46,10 +46,10 @@ class SetupFragmentLanguage : Fragment() {
super.onViewCreated(view, savedInstanceState)
// We don't want a crash for all users
- normalSafeApiCall {
+ safe {
fixPaddingStatusbar(binding?.setupRoot)
- val ctx = context ?: return@normalSafeApiCall
+ val ctx = context ?: return@safe
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val arrayAdapter =
@@ -57,7 +57,7 @@ class SetupFragmentLanguage : Fragment() {
binding?.apply {
// Icons may crash on some weird android versions?
- normalSafeApiCall {
+ safe {
val drawable = when {
BuildConfig.DEBUG -> R.drawable.cloud_2_gradient_debug
BuildConfig.FLAVOR == "prerelease" -> R.drawable.cloud_2_gradient_beta
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
index 9de6038e3..85eabefa4 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt
@@ -12,7 +12,7 @@ import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupLayoutBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import org.acra.ACRA
@@ -40,8 +40,8 @@ class SetupFragmentLayout : Fragment() {
super.onViewCreated(view, savedInstanceState)
fixPaddingStatusbar(binding?.setupRoot)
- normalSafeApiCall {
- val ctx = context ?: return@normalSafeApiCall
+ safe {
+ val ctx = context ?: return@safe
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentMedia.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentMedia.kt
index 49a93608c..9db967dcb 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentMedia.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentMedia.kt
@@ -13,7 +13,7 @@ import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.databinding.FragmentSetupMediaBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
@@ -39,10 +39,10 @@ class SetupFragmentMedia : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- normalSafeApiCall {
+ safe {
fixPaddingStatusbar(binding?.setupRoot)
- val ctx = context ?: return@normalSafeApiCall
+ val ctx = context ?: return@safe
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
val arrayAdapter =
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentProviderLanguage.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentProviderLanguage.kt
index c12e9eb83..353e735e9 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentProviderLanguage.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentProviderLanguage.kt
@@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.AllLanguagesName
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.databinding.FragmentSetupProviderLanguagesBinding
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.utils.AppContextUtils.getApiProviderLangSettings
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
@@ -42,8 +42,8 @@ class SetupFragmentProviderLanguage : Fragment() {
super.onViewCreated(view, savedInstanceState)
fixPaddingStatusbar(binding?.setupRoot)
- normalSafeApiCall {
- val ctx = context ?: return@normalSafeApiCall
+ safe {
+ val ctx = context ?: return@safe
val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
index 34096f76a..28b194531 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppContextUtils.kt
@@ -73,7 +73,7 @@ import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.isMovieType
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.plugins.RepositoryManager
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.APP_STRING_RESUME_WATCHING
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
@@ -134,9 +134,9 @@ object AppContextUtils {
//fun Context.deleteFavorite(data: SearchResponse) {
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
- // normalSafeApiCall {
+ // safe {
// val existingId =
- // getWatchNextProgramByVideoId(data.url, this).second ?: return@normalSafeApiCall
+ // getWatchNextProgramByVideoId(data.url, this).second ?: return@safe
// contentResolver.delete(
//
// TvContractCompat.buildWatchNextProgramUri(existingId),
@@ -655,7 +655,7 @@ object AppContextUtils {
fun openWebView(fragment: Fragment?, url: String) {
if (fragment?.context?.hasWebView() == true)
- normalSafeApiCall {
+ safe {
fragment
.findNavController()
.navigate(R.id.navigation_webview, WebviewFragment.newInstance(url))
diff --git a/library/src/androidMain/kotlin/com/lagradost/cloudstream3/network/WebViewResolver.android.kt b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/network/WebViewResolver.android.kt
index 0fbc5749d..fc11c4721 100644
--- a/library/src/androidMain/kotlin/com/lagradost/cloudstream3/network/WebViewResolver.android.kt
+++ b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/network/WebViewResolver.android.kt
@@ -10,7 +10,7 @@ import com.lagradost.api.getContext
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.debugException
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf
@@ -281,7 +281,7 @@ fun WebResourceRequest.toRequest(): Request? {
// If invalid url then it can crash with
// java.lang.IllegalArgumentException: Expected URL scheme 'http' or 'https' but was 'data'
// At Request.Builder().url(addParamsToUrl(url, params))
- return normalSafeApiCall {
+ return safe {
requestCreator(
this.method,
webViewUrl,
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt
index 8ee632909..beb954035 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainAPI.kt
@@ -11,7 +11,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.kotlinModule
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.syncproviders.SyncIdName
import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson
@@ -1373,10 +1373,10 @@ interface LoadResponse {
database: SimklSyncServices,
id: String?
) {
- normalSafeApiCall {
+ safe {
this.syncData[simklIdPrefix] =
addIdToString(this.syncData[simklIdPrefix], database, id.toString())
- ?: return@normalSafeApiCall
+ ?: return@safe
}
}
@@ -1394,13 +1394,13 @@ interface LoadResponse {
}
fun LoadResponse.getImdbId(): String? {
- return normalSafeApiCall {
+ return safe {
readIdFromString(this.syncData[simklIdPrefix])[SimklSyncServices.Imdb]
}
}
fun LoadResponse.getTMDbId(): String? {
- return normalSafeApiCall {
+ return safe {
readIdFromString(this.syncData[simklIdPrefix])[SimklSyncServices.Tmdb]
}
}
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Pelisplus.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Pelisplus.kt
index ef10f46ef..c871280a2 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Pelisplus.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Pelisplus.kt
@@ -3,7 +3,7 @@ package com.lagradost.cloudstream3.extractors
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.app
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.INFER_TYPE
import com.lagradost.cloudstream3.utils.extractorApis
@@ -44,7 +44,7 @@ open class Pelisplus(val mainUrl: String) {
val extractorUrl = getExtractorUrl(id)
/** Stolen from GogoanimeProvider.kt extractor */
- suspendSafeApiCall {
+ safeAsync {
val link = getDownloadUrl(id)
println("Generated vidstream download link: $link")
val page = app.get(link, referer = extractorUrl)
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
index 4109196f5..a16d41943 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/helper/GogoHelper.kt
@@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.base64Decode
import com.lagradost.cloudstream3.base64DecodeArray
import com.lagradost.cloudstream3.base64Encode
-import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.utils.AppUtils
import com.lagradost.cloudstream3.utils.ExtractorLink
@@ -25,7 +25,7 @@ object GogoHelper {
* @return the encryption key
* */
private fun getKey(id: String): String? {
- return normalSafeApiCall {
+ return safe {
id.map {
it.code.toString(16)
}.joinToString("").substring(0, 32)
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt
index bc646a8d2..2a8524e00 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt
@@ -1,7 +1,7 @@
package com.lagradost.cloudstream3.metaproviders
import com.lagradost.cloudstream3.MainAPI
-import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall
+import com.lagradost.cloudstream3.mvvm.safeAsync
import com.lagradost.cloudstream3.syncproviders.SyncIdName
object SyncRedirector {
@@ -44,7 +44,7 @@ object SyncRedirector {
return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) ->
if (providerApi.supportedSyncNames.contains(syncName)) {
syncRegex.find(url)?.value?.let {
- suspendSafeApiCall {
+ safeAsync {
providerApi.getLoadUrl(syncName, it)
}
}
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 67197e3e6..2ff3cf002 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -1309,7 +1309,7 @@ abstract class ExtractorApi {
var sourcePlugin: String? = null
//suspend fun getSafeUrl(url: String, referer: String? = null): List? {
- // return suspendSafeApiCall { getUrl(url, referer) }
+ // return safeAsync { getUrl(url, referer) }
//}
// this is the new extractorapi, override to add subtitles and stuff
From 63aaba29934a3a6188ea1a7f2fb70f0664d31eed Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 26 Jun 2025 23:44:52 +0200
Subject: [PATCH 247/956] Chore: Replaced all instances of outdated mvvm apis
(amap)
---
.../lagradost/cloudstream3/MainActivity.kt | 8 +++--
.../cloudstream3/plugins/PluginManager.kt | 30 +++++++++----------
.../services/SubscriptionWorkManager.kt | 8 ++---
3 files changed, 24 insertions(+), 22 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index 682e69e34..7f5c499ff 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -305,9 +305,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
// This specific intent is used for the gradle deployWithAdb
// https://github.com/recloudstream/gradle/blob/master/src/main/kotlin/com/lagradost/cloudstream3/gradle/tasks/DeployWithAdbTask.kt#L46
if (str == "$APP_STRING:") {
- PluginManager.___DO_NOT_CALL_FROM_A_PLUGIN_hotReloadAllLocalPlugins(
- activity
- )
+ ioSafe {
+ PluginManager.___DO_NOT_CALL_FROM_A_PLUGIN_hotReloadAllLocalPlugins(
+ activity
+ )
+ }
}
} else if (safeURI(str)?.scheme == APP_STRING_REPO) {
val url = str.replaceFirst(APP_STRING_REPO, "https")
diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
index 6e59696ae..1cffa7c1b 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt
@@ -35,7 +35,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.actions.VideoClickAction
import com.lagradost.cloudstream3.actions.VideoClickActionHolder
-import com.lagradost.cloudstream3.apmap
+import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.safe
@@ -265,7 +265,7 @@ object PluginManager {
level = DeprecationLevel.ERROR
)
@Throws
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_updateAllOnlinePluginsAndLoadThem(activity: Activity) {
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_updateAllOnlinePluginsAndLoadThem(activity: Activity) {
assertNonRecursiveCallstack()
// Load all plugins as fast as possible!
@@ -275,7 +275,7 @@ object PluginManager {
val urls = (getKey>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
- val onlinePlugins = urls.toList().apmap {
+ val onlinePlugins = urls.toList().amap {
getRepoPlugins(it.url)?.toList() ?: emptyList()
}.flatten().distinctBy { it.second.url }
@@ -296,7 +296,7 @@ object PluginManager {
val updatedPlugins = mutableListOf()
- outdatedPlugins.apmap { pluginData ->
+ outdatedPlugins.amap { pluginData ->
if (pluginData.isDisabled) {
//updatedPlugins.add(activity.getString(R.string.single_plugin_disabled, pluginData.onlineData.second.name))
unloadPlugin(pluginData.savedData.filePath)
@@ -346,7 +346,7 @@ object PluginManager {
level = DeprecationLevel.ERROR
)
@Throws
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_downloadNotExistingPluginsAndLoad(
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_downloadNotExistingPluginsAndLoad(
activity: Activity,
mode: AutoDownloadMode
) {
@@ -355,7 +355,7 @@ object PluginManager {
val newDownloadPlugins = mutableListOf()
val urls = (getKey>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
- val onlinePlugins = urls.toList().apmap {
+ val onlinePlugins = urls.toList().amap {
getRepoPlugins(it.url)?.toList() ?: emptyList()
}.flatten().distinctBy { it.second.url }
@@ -415,7 +415,7 @@ object PluginManager {
}
//Log.i(TAG, "notDownloadedPlugins => ${notDownloadedPlugins.toJson()}")
- notDownloadedPlugins.apmap { pluginData ->
+ notDownloadedPlugins.amap { pluginData ->
downloadPlugin(
activity,
pluginData.onlineData.second.url,
@@ -460,11 +460,11 @@ object PluginManager {
level = DeprecationLevel.ERROR
)
@Throws
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_loadAllOnlinePlugins(context: Context) {
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_loadAllOnlinePlugins(context: Context) {
assertNonRecursiveCallstack()
// Load all plugins as fast as possible!
- (getPluginsOnline()).toList().apmap { pluginData ->
+ (getPluginsOnline()).toList().amap { pluginData ->
loadPlugin(
context,
File(pluginData.filePath),
@@ -486,7 +486,7 @@ object PluginManager {
replaceWith = ReplaceWith("loadPlugin"),
level = DeprecationLevel.ERROR
)
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_hotReloadAllLocalPlugins(activity: FragmentActivity?) {
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_hotReloadAllLocalPlugins(activity: FragmentActivity?) {
assertNonRecursiveCallstack()
Log.d(TAG, "Reloading all local plugins!")
@@ -511,7 +511,7 @@ object PluginManager {
level = DeprecationLevel.ERROR
)
@Throws
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_loadAllLocalPlugins(context: Context, forceReload: Boolean) {
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_loadAllLocalPlugins(context: Context, forceReload: Boolean) {
assertNonRecursiveCallstack()
val dir = File(LOCAL_PLUGINS_PATH)
@@ -540,7 +540,7 @@ object PluginManager {
// Make sure all local plugins are fully refreshed.
removeKey(PLUGINS_KEY_LOCAL)
- sortedPlugins?.sortedBy { it.name }?.apmap { file ->
+ sortedPlugins?.sortedBy { it.name }?.amap { file ->
try {
val destinationFile = File(pluginDirectory, file.name)
@@ -815,7 +815,7 @@ object PluginManager {
replaceWith = ReplaceWith("loadPlugin"),
level = DeprecationLevel.ERROR
)
- fun ___DO_NOT_CALL_FROM_A_PLUGIN_manuallyReloadAndUpdatePlugins(activity: Activity) {
+ suspend fun ___DO_NOT_CALL_FROM_A_PLUGIN_manuallyReloadAndUpdatePlugins(activity: Activity) {
assertNonRecursiveCallstack()
showToast(activity.getString(R.string.starting_plugin_update_manually), Toast.LENGTH_LONG)
@@ -825,7 +825,7 @@ object PluginManager {
val urls = (getKey>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
- val onlinePlugins = urls.toList().apmap {
+ val onlinePlugins = urls.toList().amap {
getRepoPlugins(it.url)?.toList() ?: emptyList()
}.flatten().distinctBy { it.second.url }
@@ -839,7 +839,7 @@ object PluginManager {
val updatedPlugins = mutableListOf()
- allPlugins.apmap { pluginData ->
+ allPlugins.amap { pluginData ->
if (pluginData.isDisabled) {
Log.e(
"PluginManager",
diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
index c30d1208b..fc31c1f3e 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt
@@ -131,15 +131,15 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete
PluginManager.___DO_NOT_CALL_FROM_A_PLUGIN_loadAllOnlinePlugins(context)
PluginManager.___DO_NOT_CALL_FROM_A_PLUGIN_loadAllLocalPlugins(context, false)
- subscriptions.apmap { savedData ->
+ subscriptions.amap { savedData ->
try {
- val id = savedData.id ?: return@apmap null
- val api = getApiFromNameNull(savedData.apiName) ?: return@apmap null
+ val id = savedData.id ?: return@amap null
+ val api = getApiFromNameNull(savedData.apiName) ?: return@amap null
// Reasonable timeout to prevent having this worker run forever.
val response = withTimeoutOrNull(60_000) {
api.load(savedData.url) as? EpisodeResponse
- } ?: return@apmap null
+ } ?: return@amap null
val dubPreference =
getDub(id) ?: if (
From 6609a11a23fce164f07218dca94b4aa1cd3d5345 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Fri, 27 Jun 2025 00:03:57 +0200
Subject: [PATCH 248/956] Feat: Minor change to skip loading to show how many
links are loaded
---
.../cloudstream3/ui/player/GeneratorPlayer.kt | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
index 0eb28e5b5..e24fc4ec3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt
@@ -1939,6 +1939,7 @@ class GeneratorPlayer : FullScreenPlayer() {
}
}
+ @SuppressLint("SetTextI18n")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var langFilterList = listOf()
@@ -2022,7 +2023,16 @@ class GeneratorPlayer : FullScreenPlayer() {
currentLinks = it
val turnVisible = it.isNotEmpty() && lastUsedGenerator?.canSkipLoading == true
val wasGone = binding?.overlayLoadingSkipButton?.isGone == true
- binding?.overlayLoadingSkipButton?.isVisible = turnVisible
+
+ binding?.overlayLoadingSkipButton?.apply {
+ isVisible = turnVisible
+ val value = viewModel.currentLinks.value
+ if (value.isNullOrEmpty()) {
+ setText(R.string.skip_loading)
+ } else {
+ text = "${context.getString(R.string.skip_loading)} (${value.size})"
+ }
+ }
safe {
if (currentLinks.any { link ->
From 76c80e2c89a0ec9b36ae68408eb1277620a8bf73 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Fri, 27 Jun 2025 18:14:44 +0200
Subject: [PATCH 249/956] Feat: Added skip loading for acquire links, and
updated UI for the dialog
---
.../ui/result/ResultFragmentPhone.kt | 38 +++++----
.../ui/result/ResultFragmentTv.kt | 17 +++-
.../ui/result/ResultViewModel2.kt | 58 +++++++++-----
app/src/main/res/layout/bottom_loading.xml | 80 +++++++++++++------
4 files changed, 130 insertions(+), 63 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
index bd830aa2b..3d992d873 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt
@@ -2,7 +2,6 @@ package com.lagradost.cloudstream3.ui.result
import android.annotation.SuppressLint
import android.app.Dialog
-import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Rect
@@ -19,6 +18,7 @@ import android.widget.AbsListView
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.core.view.isGone
+import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.core.widget.doOnTextChanged
@@ -30,6 +30,7 @@ import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.gms.cast.framework.CastContext
import com.google.android.gms.cast.framework.CastState
import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.DubStatus
@@ -37,16 +38,15 @@ import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse
-import com.lagradost.cloudstream3.databinding.BottomSelectionDialogBinding
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
import com.lagradost.cloudstream3.databinding.FragmentResultSwipeBinding
import com.lagradost.cloudstream3.databinding.ResultRecommendationsBinding
import com.lagradost.cloudstream3.databinding.ResultSyncBinding
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
-import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.mvvm.observeNullable
+import com.lagradost.cloudstream3.mvvm.safe
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
@@ -1072,20 +1072,27 @@ open class ResultFragmentPhone : FullScreenPlayer() {
loadingDialog = null
}
loadingDialog = loadingDialog ?: context?.let { ctx ->
- val builder =
- BottomSheetDialog(ctx)
+ val builder = BottomSheetDialog(ctx)
builder.setContentView(R.layout.bottom_loading)
builder.setOnDismissListener {
loadingDialog = null
viewModel.cancelLinks()
}
- //builder.setOnCancelListener {
- // it?.dismiss()
- //}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
}
+ loadingDialog?.findViewById(R.id.overlay_loading_skip_button)?.apply {
+ if (load.linksLoaded <= 0) {
+ isInvisible = true
+ } else {
+ setOnClickListener {
+ viewModel.skipLoading()
+ }
+ isVisible = true
+ text = "${context.getString(R.string.skip_loading)} (${load.linksLoaded})"
+ }
+ }
}
observeNullable(viewModel.selectedSeason) { text ->
@@ -1127,13 +1134,14 @@ open class ResultFragmentPhone : FullScreenPlayer() {
observe(viewModel.dubSubSelections) { range ->
resultBinding?.resultDubSelect?.setOnClickListener { view ->
view?.context?.let { ctx ->
- view.popupMenuNoIconsAndNoStringRes(range
- .mapNotNull { (text, status) ->
- Pair(
- status.ordinal,
- text?.asStringNull(ctx) ?: return@mapNotNull null
- )
- }) {
+ view.popupMenuNoIconsAndNoStringRes(
+ range
+ .mapNotNull { (text, status) ->
+ Pair(
+ status.ordinal,
+ text?.asStringNull(ctx) ?: return@mapNotNull null
+ )
+ }) {
viewModel.changeDubStatus(DubStatus.entries[itemId])
}
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
index a91f513e6..ebbe0cd8f 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt
@@ -11,12 +11,14 @@ import android.view.animation.DecelerateInterpolator
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone
+import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.button.MaterialButton
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse
@@ -753,14 +755,21 @@ class ResultFragmentTv : Fragment() {
loadingDialog = null
viewModel.cancelLinks()
}
- //builder.setOnCancelListener {
- // it?.dismiss()
- //}
builder.setCanceledOnTouchOutside(true)
builder.show()
builder
}
-
+ loadingDialog?.findViewById(R.id.overlay_loading_skip_button)?.apply {
+ if (load.linksLoaded <= 0) {
+ isInvisible = true
+ } else {
+ setOnClickListener {
+ viewModel.skipLoading()
+ }
+ isVisible = true
+ text = "${context.getString(R.string.skip_loading)} (${load.linksLoaded})"
+ }
+ }
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
index 0e7e1fdb6..bf31969f3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt
@@ -235,9 +235,9 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
R.string.cast_format,
actors?.joinToString { it.actor.name }),
plotText =
- if (plot.isNullOrBlank()) txt(if (this is TorrentLoadResponse) R.string.torrent_no_plot else R.string.normal_no_plot) else txt(
- plot!!
- ),
+ if (plot.isNullOrBlank()) txt(if (this is TorrentLoadResponse) R.string.torrent_no_plot else R.string.normal_no_plot) else txt(
+ plot!!
+ ),
backgroundPosterUrl = backgroundPosterUrl,
title = name,
typeText = txt(
@@ -274,7 +274,7 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
}
),
metaText =
- if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
+ if (repo.providerType == ProviderType.MetaProvider) txt(R.string.provider_info_meta) else null,
durationText = if (dur == null || dur <= 0) null else txt(
secondsToReadable(dur * 60, "0 mins")
),
@@ -288,9 +288,9 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData {
)
} else null,
noEpisodesFoundText =
- if ((this is TvSeriesLoadResponse && this.episodes.isEmpty()) || (this is AnimeLoadResponse && !this.episodes.any { it.value.isNotEmpty() })) txt(
- R.string.no_episodes_found
- ) else null
+ if ((this is TvSeriesLoadResponse && this.episodes.isEmpty()) || (this is AnimeLoadResponse && !this.episodes.any { it.value.isNotEmpty() })) txt(
+ R.string.no_episodes_found
+ ) else null
)
}
@@ -1306,15 +1306,22 @@ class ResultViewModel2 : ViewModel() {
) {
currentLoadLinkJob?.cancel()
currentLoadLinkJob = ioSafe {
- val links = loadLinks(
- result,
- isVisible = isVisible,
- sourceTypes = sourceTypes,
- clearCache = clearCache,
- isCasting = isCasting
- )
- if (!this.isActive) return@ioSafe
- work(links)
+ val parentJob = this.coroutineContext.job
+ launch {
+ val links = loadLinks(
+ result,
+ isVisible = isVisible,
+ sourceTypes = sourceTypes,
+ clearCache = clearCache,
+ isCasting = isCasting
+ )
+ // Cancel child = skip link loading
+ // Cancel parent = dismiss dialog
+ if (parentJob.isCancelled) {
+ return@launch
+ }
+ work(links)
+ }
}
}
@@ -1358,6 +1365,11 @@ class ResultViewModel2 : ViewModel() {
}
}
+ fun skipLoading() {
+ currentLoadLinkJob?.cancelChildren()
+ currentLoadLinkJob = null
+ }
+
private suspend fun CoroutineScope.loadLinks(
result: ResultEpisode,
isVisible: Boolean,
@@ -1391,6 +1403,8 @@ class ResultViewModel2 : ViewModel() {
},
isCasting = isCasting
)
+ } catch (e : CancellationException) {
+ // Do nothing
} catch (e: Exception) {
logError(e)
} finally {
@@ -1840,7 +1854,11 @@ class ResultViewModel2 : ViewModel() {
}
fun changeDubStatus(status: DubStatus) {
- postEpisodeRange(currentIndex?.copy(dubStatus = status), currentRange, currentSorting ?: DataStoreHelper.resultsSortingMode)
+ postEpisodeRange(
+ currentIndex?.copy(dubStatus = status),
+ currentRange,
+ currentSorting ?: DataStoreHelper.resultsSortingMode
+ )
}
fun changeRange(range: EpisodeRange) {
@@ -1848,7 +1866,11 @@ class ResultViewModel2 : ViewModel() {
}
fun changeSeason(season: Int) {
- postEpisodeRange(currentIndex?.copy(season = season), currentRange, currentSorting ?: DataStoreHelper.resultsSortingMode)
+ postEpisodeRange(
+ currentIndex?.copy(season = season),
+ currentRange,
+ currentSorting ?: DataStoreHelper.resultsSortingMode
+ )
}
fun setSort(sortType: EpisodeSortType) {
diff --git a/app/src/main/res/layout/bottom_loading.xml b/app/src/main/res/layout/bottom_loading.xml
index ab05889d0..1637aa5ad 100644
--- a/app/src/main/res/layout/bottom_loading.xml
+++ b/app/src/main/res/layout/bottom_loading.xml
@@ -1,33 +1,61 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
-
+
+
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ android:id="@+id/progressBar"
+ style="@android:style/Widget.Material.ProgressBar.Horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="15dp"
+ android:layout_gravity="center"
+ android:layout_marginBottom="-6.5dp"
+ android:indeterminate="true"
+ android:indeterminateTint="?attr/colorPrimary"
+ android:progressTint="?attr/colorPrimary"
+ android:visibility="gone" />
From 5cbbc1bd123147817c393b8b2f7aae27afa8a186 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Fri, 27 Jun 2025 19:37:39 +0200
Subject: [PATCH 250/956] Fix(TV UI): Made focus in settings highlight with
border for consistency and reliability
---
.../lagradost/cloudstream3/CommonActivity.kt | 44 +++---
.../ui/settings/SettingsAccount.kt | 10 +-
.../cloudstream3/ui/settings/SettingsUI.kt | 3 +
.../custom_preference_category_material.xml | 64 +++++++++
.../res/layout/custom_preference_material.xml | 76 ++++++++++
.../custom_preference_widget_seekbar.xml | 131 ++++++++++++++++++
app/src/main/res/values/attrs.xml | 4 +
app/src/main/res/values/styles.xml | 30 +++-
app/src/main/res/xml/settings_account.xml | 2 +-
9 files changed, 339 insertions(+), 25 deletions(-)
create mode 100644 app/src/main/res/layout/custom_preference_category_material.xml
create mode 100644 app/src/main/res/layout/custom_preference_material.xml
create mode 100644 app/src/main/res/layout/custom_preference_widget_seekbar.xml
diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
index 8fd652b9c..9a8e274f5 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt
@@ -36,6 +36,8 @@ import com.lagradost.cloudstream3.databinding.ToastBinding
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.player.PlayerEventType
import com.lagradost.cloudstream3.ui.player.Torrent
+import com.lagradost.cloudstream3.ui.settings.Globals.TV
+import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.UiText
import com.lagradost.cloudstream3.ui.settings.Globals.updateTv
import com.lagradost.cloudstream3.utils.AppContextUtils.isRtl
@@ -167,7 +169,8 @@ object CommonActivity {
toast.duration = duration ?: Toast.LENGTH_SHORT
toast.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 5.toPx)
@Suppress("DEPRECATION")
- toast.view = binding.root // FIXME Find an alternative using default Toasts since custom toasts are deprecated and won't appear with api30 set as minSDK version.
+ toast.view =
+ binding.root // FIXME Find an alternative using default Toasts since custom toasts are deprecated and won't appear with api30 set as minSDK version.
currentToast = toast
toast.show()
@@ -196,7 +199,10 @@ object CommonActivity {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
context.createConfigurationContext(config)
@Suppress("DEPRECATION")
- resources.updateConfiguration(config, resources.displayMetrics) // FIXME this should be replaced
+ resources.updateConfiguration(
+ config,
+ resources.displayMetrics
+ ) // FIXME this should be replaced
}
fun Context.updateLocale() {
@@ -222,16 +228,19 @@ object CommonActivity {
componentActivity.updateTv()
NewPipe.init(DownloaderTestImpl.getInstance())
- MainActivity.activityResultLauncher = componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode == AppCompatActivity.RESULT_OK) {
- val actionUid = getKey("last_click_action") ?: return@registerForActivityResult
- Log.d(TAG, "Loading action $actionUid result handler")
- val action = VideoClickActionHolder.getByUniqueId(actionUid) as? OpenInAppAction ?: return@registerForActivityResult
- action.onResultSafe(act, result.data)
- removeKey("last_click_action")
- removeKey("last_opened_id")
+ MainActivity.activityResultLauncher =
+ componentActivity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode == AppCompatActivity.RESULT_OK) {
+ val actionUid =
+ getKey("last_click_action") ?: return@registerForActivityResult
+ Log.d(TAG, "Loading action $actionUid result handler")
+ val action = VideoClickActionHolder.getByUniqueId(actionUid) as? OpenInAppAction
+ ?: return@registerForActivityResult
+ action.onResultSafe(act, result.data)
+ removeKey("last_click_action")
+ removeKey("last_opened_id")
+ }
}
- }
// Ask for notification permissions on Android 13
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
@@ -282,8 +291,9 @@ object CommonActivity {
fun updateTheme(act: Activity) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(act)
if (settingsManager
- .getString(act.getString(R.string.app_theme_key), "AmoledLight") == "System"
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ .getString(act.getString(R.string.app_theme_key), "AmoledLight") == "System"
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ ) {
loadThemes(act)
}
}
@@ -347,9 +357,11 @@ object CommonActivity {
else -> R.style.OverlayPrimaryColorNormal
}
+
act.theme.applyStyle(currentTheme, true)
act.theme.applyStyle(currentOverlayTheme, true)
-
+ act.updateTv()
+ if (isLayout(TV)) act.theme.applyStyle(R.style.AppThemeTvOverlay, true)
act.theme.applyStyle(
R.style.LoadedStyle,
true
@@ -489,7 +501,7 @@ object CommonActivity {
}
- fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?) : Boolean? {
+ fun onKeyDown(act: Activity?, keyCode: Int, event: KeyEvent?): Boolean? {
// 149 keycode_numpad 5
val playerEvent = when (keyCode) {
@@ -560,7 +572,7 @@ object CommonActivity {
else -> return null
}
val listener = playerEventListener
- if(listener != null) {
+ if (listener != null) {
listener.invoke(playerEvent)
return true
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
index daeab0500..ebcc61b67 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt
@@ -9,12 +9,13 @@ import android.view.inputmethod.EditorInfo
import android.widget.TextView
import androidx.annotation.UiThread
import androidx.appcompat.app.AlertDialog
+import androidx.core.content.edit
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
-import androidx.preference.SwitchPreferenceCompat
+import androidx.preference.SwitchPreference
import androidx.recyclerview.widget.RecyclerView
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent
@@ -366,9 +367,10 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricCallback {
private fun updateAuthPreference(enabled: Boolean) {
val biometricKey = getString(R.string.biometric_key)
- PreferenceManager.getDefaultSharedPreferences(context ?: return).edit()
- .putBoolean(biometricKey, enabled).apply()
- findPreference(biometricKey)?.isChecked = enabled
+ PreferenceManager.getDefaultSharedPreferences(context ?: return).edit {
+ putBoolean(biometricKey, enabled)
+ }
+ findPreference(biometricKey)?.isChecked = enabled
}
override fun onAuthenticationError() {
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
index 98db81e6a..6446ae75d 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt
@@ -14,6 +14,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
+import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.updateTv
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.hideOn
@@ -39,6 +40,8 @@ class SettingsUI : PreferenceFragmentCompat() {
setPreferencesFromResource(R.xml.settings_ui, rootKey)
val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext())
+ getPref(R.string.random_button_key)?.hideOn(EMULATOR or TV)
+
(getPref(R.string.overscan_key)?.hideOn(PHONE or EMULATOR) as? SeekBarPreference)?.setOnPreferenceChangeListener { perf, newValue ->
val padding = (newValue as? Int)?.toPx ?: return@setOnPreferenceChangeListener true
(perf.context.getActivity() as? MainActivity)?.binding?.homeRoot?.setPadding(padding, padding, padding, padding)
diff --git a/app/src/main/res/layout/custom_preference_category_material.xml b/app/src/main/res/layout/custom_preference_category_material.xml
new file mode 100644
index 000000000..06db99017
--- /dev/null
+++ b/app/src/main/res/layout/custom_preference_category_material.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/custom_preference_material.xml b/app/src/main/res/layout/custom_preference_material.xml
new file mode 100644
index 000000000..0ab98c22b
--- /dev/null
+++ b/app/src/main/res/layout/custom_preference_material.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/custom_preference_widget_seekbar.xml b/app/src/main/res/layout/custom_preference_widget_seekbar.xml
new file mode 100644
index 000000000..02c5ec1be
--- /dev/null
+++ b/app/src/main/res/layout/custom_preference_widget_seekbar.xml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index 7b90beb0c..c7af48e08 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -13,6 +13,10 @@
- ?attr/colorPrimary
+
+
+
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7ff14f8c6..05d87f1ce 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -67,16 +67,19 @@
- @color/iconColor
- @color/white
- @color/black
-
+ - ?android:attr/selectableItemBackground
- @style/CustomPreferenceThemeOverlay
-
- ?attr/white
- ?attr/white
- ?attr/white
- 1.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - ?attr/textColor
+ - 10sp
+
+ - @drawable/kid_star_24px
+
+
+
+
+
+
+
+
+
From c783e82c80a1f8cf7577db4e29cc44b64e5b3edf Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 2 Oct 2025 19:06:24 +0200
Subject: [PATCH 378/956] Fix: Minor typo + testing fix
---
.../java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt | 2 +-
app/src/main/res/layout/fragment_result_tv.xml | 2 +-
app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
index 3d5367fc6..4c5cdea5b 100644
--- a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
+++ b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt
@@ -143,7 +143,7 @@ class ExampleInstrumentedTest {
Assert.assertTrue("Api does not contain a name", api.name != "NONE")
Assert.assertTrue(
"Api ${api.name} does not contain a valid language code",
- langTagsIETF.contains(api.lang, ignoreCase = true)
+ langTagsIETF.contains(api.lang)
)
Assert.assertTrue(
"Api ${api.name} does not contain any supported types",
diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml
index 461a712d9..dbebecfcc 100644
--- a/app/src/main/res/layout/fragment_result_tv.xml
+++ b/app/src/main/res/layout/fragment_result_tv.xml
@@ -285,7 +285,7 @@ https://developer.android.com/design/ui/tv/samples/jet-fit
android:layout_gravity="center_vertical"
tools:text="69m\nremaining" />
- e
+
Date: Thu, 2 Oct 2025 19:16:13 +0200
Subject: [PATCH 379/956] Chore: Cleanup unused xml files
---
app/src/main/res/layout/player_episodes.xml | 8 --
.../main/res/layout/player_episodes_large.xml | 105 ----------------
.../main/res/layout/player_episodes_small.xml | 56 ---------
.../res/layout/result_episode_both_old.xml | 14 ---
.../res/layout/result_episode_both_tv_old.xml | 21 ----
.../layout/result_episode_large_tv_old.xml | 112 ------------------
.../main/res/layout/result_episode_tv_old.xml | 59 ---------
7 files changed, 375 deletions(-)
delete mode 100644 app/src/main/res/layout/player_episodes.xml
delete mode 100644 app/src/main/res/layout/player_episodes_large.xml
delete mode 100644 app/src/main/res/layout/player_episodes_small.xml
delete mode 100644 app/src/main/res/layout/result_episode_both_old.xml
delete mode 100644 app/src/main/res/layout/result_episode_both_tv_old.xml
delete mode 100644 app/src/main/res/layout/result_episode_large_tv_old.xml
delete mode 100644 app/src/main/res/layout/result_episode_tv_old.xml
diff --git a/app/src/main/res/layout/player_episodes.xml b/app/src/main/res/layout/player_episodes.xml
deleted file mode 100644
index a491bc096..000000000
--- a/app/src/main/res/layout/player_episodes.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_episodes_large.xml b/app/src/main/res/layout/player_episodes_large.xml
deleted file mode 100644
index 476965635..000000000
--- a/app/src/main/res/layout/player_episodes_large.xml
+++ /dev/null
@@ -1,105 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/player_episodes_small.xml b/app/src/main/res/layout/player_episodes_small.xml
deleted file mode 100644
index 62dd4bca1..000000000
--- a/app/src/main/res/layout/player_episodes_small.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode_both_old.xml b/app/src/main/res/layout/result_episode_both_old.xml
deleted file mode 100644
index 6472ecc10..000000000
--- a/app/src/main/res/layout/result_episode_both_old.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode_both_tv_old.xml b/app/src/main/res/layout/result_episode_both_tv_old.xml
deleted file mode 100644
index f273a1180..000000000
--- a/app/src/main/res/layout/result_episode_both_tv_old.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode_large_tv_old.xml b/app/src/main/res/layout/result_episode_large_tv_old.xml
deleted file mode 100644
index 3a7cef3ca..000000000
--- a/app/src/main/res/layout/result_episode_large_tv_old.xml
+++ /dev/null
@@ -1,112 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/result_episode_tv_old.xml b/app/src/main/res/layout/result_episode_tv_old.xml
deleted file mode 100644
index 62546cf94..000000000
--- a/app/src/main/res/layout/result_episode_tv_old.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
From 373d746a3f0baeba695c6496a300f90fa5e404a4 Mon Sep 17 00:00:00 2001
From: rockhero1234 <149141736+rockhero1234@users.noreply.github.com>
Date: Thu, 2 Oct 2025 23:04:48 +0530
Subject: [PATCH 380/956] feat(library):scroll top on sort (#1938)
---
.../com/lagradost/cloudstream3/ui/library/LibraryFragment.kt | 1 +
.../com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
index bfac72067..7555c2aca 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt
@@ -416,6 +416,7 @@ class LibraryFragment : Fragment() {
libraryViewModel.currentPage.value?.let { page ->
binding?.viewpager?.setCurrentItem(page, false)
+ binding?.searchBar?.setExpanded(true)
}
updateRandom()
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
index 0110187f6..392bf9cb3 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt
@@ -58,7 +58,6 @@ class ViewpagerAdapter(
LibraryViewpagerPageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
}
-
override fun onUpdateContent(
holder: ViewHolderState,
item: SyncAPI.Page,
@@ -67,6 +66,7 @@ class ViewpagerAdapter(
val binding = holder.view
if (binding !is LibraryViewpagerPageBinding) return
(binding.pageRecyclerview.adapter as? PageAdapter)?.updateList(item.items)
+ binding.pageRecyclerview.scrollToPosition(0)
}
override fun onBindContent(holder: ViewHolderState, item: SyncAPI.Page, position: Int) {
From 6d4c530e16d90ee946efa4ce6599efe4140e0760 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 2 Oct 2025 19:52:08 +0200
Subject: [PATCH 381/956] Chore: Bump media3 to 1.8.0
---
.../cloudstream3/ui/player/UpdatedMatroskaExtractor.kt | 2 +-
gradle/libs.versions.toml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/UpdatedMatroskaExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/UpdatedMatroskaExtractor.kt
index 06b4c12c3..016e7d203 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/UpdatedMatroskaExtractor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/UpdatedMatroskaExtractor.kt
@@ -41,7 +41,7 @@ import androidx.media3.container.NalUnitUtil
import androidx.media3.extractor.AacUtil
import androidx.media3.extractor.AvcConfig
import androidx.media3.extractor.ChunkIndex
-import androidx.media3.extractor.DolbyVisionConfig
+import androidx.media3.container.DolbyVisionConfig
import androidx.media3.extractor.Extractor
import androidx.media3.extractor.ExtractorInput
import androidx.media3.extractor.ExtractorOutput
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7a3fd9165..987ad3cd6 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -24,7 +24,7 @@ kotlinxCoroutinesCore = "1.10.1"
lifecycleLivedataKtx = "2.8.7"
lifecycleViewmodelKtx = "2.8.7"
material = "1.12.0"
-media3 = "1.6.1"
+media3 = "1.8.0"
navigationKtx = "2.8.9"
newpipeextractor = "v0.24.6"
nextlibMedia3 = "0.8.4"
From 26570d688fce0cc4d8c51c32135dfa7575b1b476 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Thu, 2 Oct 2025 19:54:54 +0200
Subject: [PATCH 382/956] Chore: Bump versionName
---
app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 080f9dd2b..b40b19f20 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -63,7 +63,7 @@ android {
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 66
- versionName = "4.5.5"
+ versionName = "4.5.6"
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
resValue("string", "commit_hash", getGitCommitHash())
From ccf3b0508841782e7db7e2530b01ba1ffc72dd9b Mon Sep 17 00:00:00 2001
From: Phisher98 <153359846+phisher98@users.noreply.github.com>
Date: Fri, 3 Oct 2025 21:05:44 +0530
Subject: [PATCH 383/956] Dailymotion Fix (#1947)
* Dailymotion Improvement
* Dailymotion Improvement Minor Fix
---
.../cloudstream3/extractors/Dailymotion.kt | 51 +++++++++++++------
1 file changed, 36 insertions(+), 15 deletions(-)
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt
index aa5e60c32..f15273c37 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Dailymotion.kt
@@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors
+import com.google.gson.Gson
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
@@ -8,6 +9,7 @@ import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
import java.net.URI
+
class Geodailymotion : Dailymotion() {
override val name = "GeoDailymotion"
override val mainUrl = "https://geo.dailymotion.com"
@@ -30,29 +32,26 @@ open class Dailymotion : ExtractorApi() {
val embedUrl = getEmbedUrl(url) ?: return
val id = getVideoId(embedUrl) ?: return
val metaDataUrl = "$baseUrl/player/metadata/video/$id"
+
val response = app.get(metaDataUrl, referer = embedUrl).text
- val qualityUrlRegex = Regex(""""url"\s*:\s*"([^"]+)"""")
- val subtitlesRegex = Regex(""""subtitles"\s*:\s*\{[^}]*"data"\s*:\s*(\[[^\]]*\])""")
+ val gson = Gson()
+ val meta = gson.fromJson(response, MetaData::class.java)
- val urls = qualityUrlRegex.findAll(response)
- .map { it.groupValues[1] }
- .toList().filter { it.contains(".m3u8") }
-
- urls.forEach { videoUrl ->
- getStream(videoUrl, this.name, callback)
+ meta.qualities?.get("auto")?.forEach { quality ->
+ val videoUrl = quality.url
+ if (!videoUrl.isNullOrEmpty() && videoUrl.contains(".m3u8")) {
+ getStream(videoUrl, this.name, callback)
+ }
}
- val subtitlesMatches = subtitlesRegex.findAll(response).map { it.groupValues[1] }.toList()
- subtitlesMatches.forEach { subtitleJson ->
- val subRegex = Regex("""\{\s*"label"\s*:\s*"([^"]+)",\s*"urls"\s*:\s*\["([^"]+)"""")
- subRegex.findAll(subtitleJson).forEach { match ->
- val label = match.groupValues[1]
- val subUrl = match.groupValues[2]
- subtitleCallback(SubtitleFile(label, subUrl))
+ meta.subtitles?.data?.forEach { (_, subData) ->
+ subData.urls.forEach { subUrl ->
+ subtitleCallback(SubtitleFile(subData.label, subUrl))
}
}
}
+
private fun getEmbedUrl(url: String): String? {
if (url.contains("/embed/") || url.contains("/video/")) return url
if (url.contains("geo.dailymotion.com")) {
@@ -76,4 +75,26 @@ open class Dailymotion : ExtractorApi() {
) {
return generateM3u8(name, streamLink, "").forEach(callback)
}
+
+
+ data class MetaData(
+ val qualities: Map>?,
+ val subtitles: SubtitlesWrapper?
+ )
+
+ data class Quality(
+ val type: String?,
+ val url: String?
+ )
+
+ data class SubtitlesWrapper(
+ val enable: Boolean,
+ val data: Map?
+ )
+
+ data class SubtitleData(
+ val label: String,
+ val urls: List
+ )
+
}
\ No newline at end of file
From 6632f764b0cdb53a7e843057916cf524fa862cf7 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Fri, 3 Oct 2025 22:22:39 +0200
Subject: [PATCH 384/956] Feat: New TV UI for Homepage (#1942)
---
.../lagradost/cloudstream3/MainActivity.kt | 41 ++-
.../ui/home/HomeParentItemAdapterPreview.kt | 66 ++--
app/src/main/res/layout/activity_main_tv.xml | 9 +-
.../main/res/layout/fragment_home_head_tv.xml | 347 +++++++++---------
app/src/main/res/layout/fragment_home_tv.xml | 2 +-
app/src/main/res/layout/rail_header.xml | 2 +-
app/src/main/res/values/dimens.xml | 2 +-
7 files changed, 244 insertions(+), 225 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
index c8d9d6a8b..4a3886d50 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt
@@ -404,6 +404,24 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
}
return false
}
+
+
+ fun centerView(view: View?) {
+ if (view == null) return
+ try {
+ Log.v(TAG, "centerView: $view")
+ val r = Rect(0, 0, 0, 0)
+ view.getDrawingRect(r)
+ val x = r.centerX()
+ val y = r.centerY()
+ val dx = r.width() / 2 //screenWidth / 2
+ val dy = screenHeight / 2
+ val r2 = Rect(x - dx, y - dy, x + dx, y + dy)
+ view.requestRectangleOnScreen(r2, false)
+ // TvFocus.current =TvFocus.current.copy(y=y.toFloat())
+ } catch (_: Throwable) {
+ }
+ }
}
@@ -484,7 +502,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
).contains(destination.id)
- val dontPush = listOf(
+ /*val dontPush = listOf(
R.id.navigation_home,
R.id.navigation_search,
R.id.navigation_results_phone,
@@ -515,7 +533,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
}
layoutParams = params
- }
+ }*/
val landscape = when (resources.configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
@@ -1140,23 +1158,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
}
}
- private fun centerView(view: View?) {
- if (view == null) return
- try {
- Log.v(TAG, "centerView: $view")
- val r = Rect(0, 0, 0, 0)
- view.getDrawingRect(r)
- val x = r.centerX()
- val y = r.centerY()
- val dx = r.width() / 2 //screenWidth / 2
- val dy = screenHeight / 2
- val r2 = Rect(x - dx, y - dy, x + dx, y + dy)
- view.requestRectangleOnScreen(r2, false)
- // TvFocus.current =TvFocus.current.copy(y=y.toFloat())
- } catch (_: Throwable) {
- }
- }
-
@Suppress("DEPRECATION_ERROR")
override fun onCreate(savedInstanceState: Bundle?) {
app.initClient(this)
@@ -1228,7 +1229,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricCa
if (isLayout(TV)) {
// Put here any button you don't want focusing it to center the view
val exceptionButtons = listOf(
- R.id.home_preview_play_btt,
+ //R.id.home_preview_play_btt,
R.id.home_preview_info_btt,
R.id.home_preview_hidden_next_focus,
R.id.home_preview_hidden_prev_focus,
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
index dd2bf7bbc..58167e0c2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt
@@ -108,7 +108,7 @@ class HomeParentItemAdapterPreview(
)
}
- return HeaderViewHolder(binding, viewModel,accountViewModel, fragment = fragment)
+ return HeaderViewHolder(binding, viewModel, accountViewModel, fragment = fragment)
}
override fun onBindHeader(holder: ViewHolderState) {
@@ -116,7 +116,10 @@ class HomeParentItemAdapterPreview(
}
private class HeaderViewHolder(
- val binding: ViewBinding, val viewModel: HomeViewModel,accountViewModel: AccountViewModel, fragment: Fragment,
+ val binding: ViewBinding,
+ val viewModel: HomeViewModel,
+ accountViewModel: AccountViewModel,
+ fragment: Fragment,
) :
ViewHolderState(binding) {
@@ -305,10 +308,13 @@ class HomeParentItemAdapterPreview(
itemView.findViewById(R.id.home_bookmarked_child_recyclerview)
private val headProfilePic: ImageView? = itemView.findViewById(R.id.home_head_profile_pic)
- private val headProfilePicCard: View? = itemView.findViewById(R.id.home_head_profile_padding)
+ private val headProfilePicCard: View? =
+ itemView.findViewById(R.id.home_head_profile_padding)
- private val alternateHeadProfilePic: ImageView? = itemView.findViewById(R.id.alternate_home_head_profile_pic)
- private val alternateHeadProfilePicCard: View? = itemView.findViewById(R.id.alternate_home_head_profile_padding)
+ private val alternateHeadProfilePic: ImageView? =
+ itemView.findViewById(R.id.alternate_home_head_profile_pic)
+ private val alternateHeadProfilePicCard: View? =
+ itemView.findViewById(R.id.alternate_home_head_profile_padding)
private val topPadding: View? = itemView.findViewById(R.id.home_padding)
@@ -334,7 +340,7 @@ class HomeParentItemAdapterPreview(
homePreviewTags.isGone =
item.tags.isNullOrEmpty()
- homePreviewPlayBtt.setOnClickListener { view ->
+ /*homePreviewPlayBtt.setOnClickListener { view ->
viewModel.click(
LoadClickCallback(
START_ACTION_RESUME_LATEST,
@@ -343,7 +349,7 @@ class HomeParentItemAdapterPreview(
item
)
)
- }
+ }*/
homePreviewInfoBtt.setOnClickListener { view ->
viewModel.click(
@@ -494,7 +500,7 @@ class HomeParentItemAdapterPreview(
activity?.showAccountSelectLinear()
}
- fun showAccountEditBox(context:Context): Boolean {
+ fun showAccountEditBox(context: Context): Boolean {
val currentAccount = DataStoreHelper.getCurrentAccount()
return if (currentAccount != null) {
showAccountEditDialog(
@@ -502,16 +508,21 @@ class HomeParentItemAdapterPreview(
account = currentAccount,
isNewAccount = false,
accountEditCallback = { accountViewModel.handleAccountUpdate(it, context) },
- accountDeleteCallback = { accountViewModel.handleAccountDelete(it, context) }
+ accountDeleteCallback = {
+ accountViewModel.handleAccountDelete(
+ it,
+ context
+ )
+ }
)
true
- }else false
+ } else false
}
- alternateHeadProfilePicCard?.setOnLongClickListener{
+ alternateHeadProfilePicCard?.setOnLongClickListener {
showAccountEditBox(it.context)
}
- headProfilePicCard?.setOnLongClickListener{
+ headProfilePicCard?.setOnLongClickListener {
showAccountEditBox(it.context)
}
@@ -525,8 +536,12 @@ class HomeParentItemAdapterPreview(
viewModel.loadAndCancel(api, forceReload = true, fromUI = true)
}
}
- homePreviewReloadProvider.setOnClickListener{
- viewModel.loadAndCancel(viewModel.apiName.value ?: noneApi.name, forceReload = true, fromUI = true)
+ homePreviewReloadProvider.setOnClickListener {
+ viewModel.loadAndCancel(
+ viewModel.apiName.value ?: noneApi.name,
+ forceReload = true,
+ fromUI = true
+ )
showToast(R.string.action_reload, Toast.LENGTH_SHORT)
true
}
@@ -535,14 +550,18 @@ class HomeParentItemAdapterPreview(
viewModel.queryTextSubmit("")
}
- // This makes the hidden next buttons only available when on the info button
- // Otherwise you might be able to go to the next item without being at the info button
- homePreviewInfoBtt.setOnFocusChangeListener { _, hasFocus ->
- homePreviewHiddenNextFocus.isFocusable = hasFocus
- }
-
- homePreviewPlayBtt.setOnFocusChangeListener { _, hasFocus ->
- homePreviewHiddenPrevFocus.isFocusable = hasFocus
+ // A workaround to the focus problem of always centering the view on focus
+ // as that causes higher android versions to stretch the ui when switching between shows
+ var lastFocusTimeoutMs = 0L
+ homePreviewInfoBtt.setOnFocusChangeListener { view, hasFocus ->
+ val lastFocusMs = lastFocusTimeoutMs
+ // Always reset timer, as we only want to update
+ // it if we have not interacted in half a second
+ lastFocusTimeoutMs = System.currentTimeMillis()
+ if (!hasFocus) return@setOnFocusChangeListener
+ if (lastFocusMs + 500L < System.currentTimeMillis()) {
+ MainActivity.centerView(view)
+ }
}
homePreviewHiddenNextFocus.setOnFocusChangeListener { _, hasFocus ->
@@ -560,7 +579,8 @@ class HomeParentItemAdapterPreview(
)?.requestFocus()
} else {
previewViewpager.setCurrentItem(previewViewpager.currentItem - 1, true)
- binding.homePreviewPlayBtt.requestFocus()
+ binding.homePreviewInfoBtt.requestFocus()
+ //binding.homePreviewPlayBtt.requestFocus()
}
}
}
diff --git a/app/src/main/res/layout/activity_main_tv.xml b/app/src/main/res/layout/activity_main_tv.xml
index 0003b2618..1c4ebd8ef 100644
--- a/app/src/main/res/layout/activity_main_tv.xml
+++ b/app/src/main/res/layout/activity_main_tv.xml
@@ -15,12 +15,13 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:nextFocusDown="@id/home_preview_info_btt">
+
@@ -64,8 +144,8 @@
android:padding="10dp"
android:src="@drawable/ic_refresh"
android:tag="@string/tv_no_focus_tag"
- app:tint="@color/player_on_button_tv_attr"
- android:visibility="gone"/>
+ android:visibility="gone"
+ app:tint="@color/player_on_button_tv_attr" />
@@ -130,89 +210,6 @@
android:src="@drawable/ic_outline_account_circle_24" />
-->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -252,9 +249,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/navbar_width"
android:layout_marginEnd="0dp"
+ android:background="?android:attr/selectableItemBackground"
android:padding="12dp"
android:text="@string/continue_watching"
- android:background="?android:attr/selectableItemBackground"
app:drawableTint="?attr/white" />
-
-
-
+ android:foreground="?android:attr/selectableItemBackgroundBorderless"
+ android:paddingStart="12dp"
+ android:paddingTop="5dp"
+ android:paddingEnd="12dp"
-
+
+
+ android:orientation="horizontal">
-
+ android:nextFocusUp="@id/home_watch_child_recyclerview"
+ android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
+ android:text="@string/type_watching" />
-
+ android:nextFocusUp="@id/home_watch_child_recyclerview"
+ android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
+ android:text="@string/type_plan_to_watch" />
-
+ android:nextFocusUp="@id/home_watch_child_recyclerview"
+ android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
+ android:text="@string/type_on_hold" />
-
-
-
+ android:nextFocusUp="@id/home_watch_child_recyclerview"
+ android:nextFocusDown="@id/home_bookmarked_child_recyclerview"
+ android:text="@string/type_dropped" />
-
+
+
+
+
+
+ android:nextFocusDown="@id/home_preview_info_btt" >
diff --git a/app/src/main/res/layout/rail_header.xml b/app/src/main/res/layout/rail_header.xml
index 8868b57be..16ccd6c39 100644
--- a/app/src/main/res/layout/rail_header.xml
+++ b/app/src/main/res/layout/rail_header.xml
@@ -13,7 +13,7 @@
android:layout_height="24dp"
app:itemIconTint="@color/item_select_color"
android:focusable="true"
- android:nextFocusRight="@id/home_preview_play_btt"
+ android:nextFocusRight="@id/home_preview_info_btt"
android:src="@drawable/notifications_icon_selector"
android:contentDescription="@string/account"
app:tint="@color/item_select_color" />
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index cf632e0d3..5bf1e87ed 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -16,7 +16,7 @@
2000
3dp
- 62dp
+ 0dp
50dp
1dp
From a70fb87594c71eab2396db4b77d9e8294bf993c2 Mon Sep 17 00:00:00 2001
From: firelight <147925818+fire-light42@users.noreply.github.com>
Date: Sat, 4 Oct 2025 02:02:42 +0200
Subject: [PATCH 385/956] Feat: Infinite scrolling on quicksearch and trakt
(#1949)
---
.../ui/quicksearch/QuickSearchFragment.kt | 37 ++++++++--
.../cloudstream3/ui/search/SearchAdaptor.kt | 9 +--
.../cloudstream3/ui/search/SearchFragment.kt | 5 +-
.../cloudstream3/ui/search/SearchViewModel.kt | 70 ++++++++++++-------
.../metaproviders/TraktProvider.kt | 12 ++--
5 files changed, 87 insertions(+), 46 deletions(-)
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
index d2e308a3c..f428321d2 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt
@@ -16,6 +16,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.CommonActivity.activity
@@ -39,6 +40,7 @@ import com.lagradost.cloudstream3.ui.settings.Globals.TV
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.utils.AppContextUtils.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.utils.AppContextUtils.filterSearchResultByFilmQuality
+import com.lagradost.cloudstream3.utils.AppContextUtils.isRecyclerScrollable
import com.lagradost.cloudstream3.utils.AppContextUtils.ownShow
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.UIHelper
@@ -137,7 +139,6 @@ class QuickSearchFragment : Fragment() {
HomeFragment.currentSpan = it
}
binding?.quickSearchAutofitResults?.spanCount = HomeFragment.currentSpan
- HomeFragment.currentSpan = HomeFragment.currentSpan
HomeFragment.configEvent.invoke(HomeFragment.currentSpan)
}
@@ -160,7 +161,8 @@ class QuickSearchFragment : Fragment() {
getApiFromNameNull(providers?.first())?.hasQuickSearch ?: false
} else false
- if (isSingleProvider) {
+ val firstProvider = providers?.firstOrNull()
+ if (isSingleProvider && firstProvider != null) {
binding?.quickSearchAutofitResults?.apply {
adapter = SearchAdapter(
ArrayList(),
@@ -170,9 +172,31 @@ class QuickSearchFragment : Fragment() {
}
}
+ binding?.quickSearchAutofitResults?.addOnScrollListener(object :
+ RecyclerView.OnScrollListener() {
+ var expandCount = 0
+
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ super.onScrollStateChanged(recyclerView, newState)
+
+ val adapter = recyclerView.adapter
+ if (adapter !is SearchAdapter) return
+
+ val count = adapter.itemCount
+ val currentHasNext = adapter.hasNext
+
+ if (!recyclerView.isRecyclerScrollable() && currentHasNext && expandCount != count) {
+ expandCount = count
+ ioSafe {
+ searchViewModel.expandAndReturn(firstProvider)
+ }
+ }
+ }
+ })
+
try {
binding?.quickSearch?.queryHint =
- getString(R.string.search_hint_site).format(providers?.first())
+ getString(R.string.search_hint_site).format(firstProvider)
} catch (e: Exception) {
logError(e)
}
@@ -273,9 +297,12 @@ class QuickSearchFragment : Fragment() {
when (it) {
is Resource.Success -> {
it.value.let { data ->
- (binding?.quickSearchAutofitResults?.adapter as? SearchAdapter)?.updateList(
- context?.filterSearchResultByFilmQuality(data) ?: data
+ val adapter =
+ (binding?.quickSearchAutofitResults?.adapter as? SearchAdapter)
+ adapter?.updateList(
+ context?.filterSearchResultByFilmQuality(data.list) ?: data.list
)
+ adapter?.hasNext = data.hasNext
}
searchExitIcon?.alpha = 1f
binding?.quickSearchLoadingBar?.alpha = 0f
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
index f318401c0..ed3fabe71 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt
@@ -31,7 +31,7 @@ class SearchClickCallback(
)
class SearchAdapter(
- private val cardList: MutableList,
+ private var cardList: List,
private val resView: AutofitRecyclerView,
private val clickCallback: (SearchClickCallback) -> Unit,
) : RecyclerView.Adapter() {
@@ -74,12 +74,9 @@ class SearchAdapter(
fun updateList(newList: List) {
val diffResult = DiffUtil.calculateDiff(
- SearchResponseDiffCallback(this.cardList, newList)
+ SearchResponseDiffCallback(cardList, newList)
)
-
- cardList.clear()
- cardList.addAll(newList)
-
+ cardList = newList
diffResult.dispatchUpdatesTo(this)
}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
index 2c5f80090..e8fcbc851 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt
@@ -492,8 +492,9 @@ class SearchFragment : Fragment() {
when (it) {
is Resource.Success -> {
it.value.let { data ->
- if (data.isNotEmpty()) {
- (binding?.searchAutofitResults?.adapter as? SearchAdapter)?.updateList(data)
+ val list = data.list
+ if (list.isNotEmpty()) {
+ (binding?.searchAutofitResults?.adapter as? SearchAdapter)?.updateList(list)
}
}
searchExitIcon?.alpha = 1f
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
index a0d533545..72a2bcb60 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchViewModel.kt
@@ -32,9 +32,9 @@ data class ExpandableSearchList(
const val SEARCH_HISTORY_KEY = "search_history"
class SearchViewModel : ViewModel() {
- private val _searchResponse: MutableLiveData>> =
+ private val _searchResponse: MutableLiveData> =
MutableLiveData()
- val searchResponse: LiveData>> get() = _searchResponse
+ val searchResponse: LiveData> get() = _searchResponse
private val _currentSearch: MutableLiveData
-
-
- - UTF-8
- - UTF-16
- - UTF-16BE
- - UTF-16LE
- - GB18030
- - ISO-8859-15
- - Windows-1252
- - IBM850
- - ISO-8859-2
- - Windows-1250
- - ISO-8859-3
- - ISO-8859-10
- - Windows-1251
- - KOI8-R
- - KOI8-U
- - ISO-8859-6
- - Windows-1256
- - ISO-8859-7
- - Windows-1253
- - ISO-8859-8
- - Windows-1255
- - ISO-8859-9
- - Windows-1254
- - ISO-8859-11
- - Windows-874
- - ISO-8859-13
- - Windows-1257
- - ISO-8859-14
- - ISO-8859-16
- - ISO-2022-CN-EXT
- - EUC-CN
- - ISO-2022-JP-2
- - EUC-JP
- - Shift_JIS
- - CP949
- - ISO-2022-KR
- - Big5
- - ISO-2022-TW
- - Big5-HKSCS
- - VISCII
- - Windows-1258
-
diff --git a/app/src/main/res/values-b+es/strings.xml b/app/src/main/res/values-b+es/strings.xml
index 8cd37933b..390c2df58 100644
--- a/app/src/main/res/values-b+es/strings.xml
+++ b/app/src/main/res/values-b+es/strings.xml
@@ -567,8 +567,6 @@
Editar la cuenta
Enlaces recargados
Mostrar un botón para cambiar la orientación de la pantalla
- rotate_video_key
- auto_rotate_video_key
Giro automático
Girar
Activar el cambio automático de la orientación de la pantalla en función de la orientación del vídeo
@@ -617,7 +615,6 @@
¡El código PIN ya ha caducado!
El código caduca en %1$d mín y %2$d s
No puedo obtener el código PIN del dispositivo; intente con la autenticación local
- hide_player_control_names_key
Reproducir desde el principio
Abrir vídeo de forma local
Advertencia
diff --git a/app/src/main/res/values-b+fa/strings.xml b/app/src/main/res/values-b+fa/strings.xml
index 146236651..da6f04d8e 100644
--- a/app/src/main/res/values-b+fa/strings.xml
+++ b/app/src/main/res/values-b+fa/strings.xml
@@ -190,7 +190,6 @@
پیشفرض
کارتون
تورنت
- hide_player_control_names_key
این ارائهدهنده تورنتی است، استفاده از VPN توصیه میشود
بارگزاری پرونده پشتیبانی
ارتفاع زیرنویس
diff --git a/app/src/main/res/values-b+fil/strings.xml b/app/src/main/res/values-b+fil/strings.xml
index f8ba8fa47..d4844d1d7 100644
--- a/app/src/main/res/values-b+fil/strings.xml
+++ b/app/src/main/res/values-b+fil/strings.xml
@@ -1,6 +1,5 @@
- hide_player_control_names_key
Maling PIN. Pakisubukang muli.
Alisin ang napanood hanggang sa episode na ito
Walang koneksyon sa internet.\n\nKumonekta sa internet at subukang muli, o panoorin ang iyong mga na-download habang ikaw ay offline.
diff --git a/app/src/main/res/values-b+fr/strings.xml b/app/src/main/res/values-b+fr/strings.xml
index d8ecc4dae..a4e669ccf 100644
--- a/app/src/main/res/values-b+fr/strings.xml
+++ b/app/src/main/res/values-b+fr/strings.xml
@@ -606,7 +606,6 @@
Verrouillage biométrique
Sélectionnez un appareil de diffusion
Saison %1$d Episode %2$d sera publié dans
- hide_player_control_names_key
Regarder depuis le début
Ouvrir une vidéo locale
Attention
diff --git a/app/src/main/res/values-b+gl/strings.xml b/app/src/main/res/values-b+gl/strings.xml
index aeb76080e..1b8f068e3 100644
--- a/app/src/main/res/values-b+gl/strings.xml
+++ b/app/src/main/res/values-b+gl/strings.xml
@@ -161,7 +161,6 @@
Selecciona o modo para filtrar a descarga dos complementos
Instala automáticamente todos os complementos aínda non instalados dos repositorios engadidos.
Mostrar actualizacións da aplicación
- hide_player_control_names_key
Reiniciar aos valores predefinidos
-30
Audiolibro
@@ -274,10 +273,6 @@
Ligazón copiada ó portapapeis
Reproducir capítulo
Temporada
-
- - Capítulos
-
-
%1$d-%2$d
%1$d %2$s
T
diff --git a/app/src/main/res/values-b+hi/strings.xml b/app/src/main/res/values-b+hi/strings.xml
index 8b7c79650..2c5247238 100644
--- a/app/src/main/res/values-b+hi/strings.xml
+++ b/app/src/main/res/values-b+hi/strings.xml
@@ -202,7 +202,6 @@
रूपरेखा रंग
उपशीर्षक ऊंचाई
मुद्रलिपि
- hide_player_control_names_key
वर्तमान में कोई डाउनलोड नहीं है।
आरम्भ से शुरू करें
मिटाने के लिए वस्तु चुनें
@@ -312,10 +311,6 @@
लाइब्रेरी
मीडिया
खोज परिणामों में चयनित वीडियो गुणवत्ता छुपाएं
-
- - प्रकरण
-
-
%1$d-%2$d
@string/home_play
प्लेयर में आगे पीछे जाने का समय (सेकंड्स)
diff --git a/app/src/main/res/values-b+hr/strings.xml b/app/src/main/res/values-b+hr/strings.xml
index 902a75500..1e2c344ab 100644
--- a/app/src/main/res/values-b+hr/strings.xml
+++ b/app/src/main/res/values-b+hr/strings.xml
@@ -1,15 +1,6 @@
- %d %s | %s
- %s • %s
- %s / %s
- %s %s
- +%d
- -%d
- %d
- %d
- %d
%1$s epizoda %2$d
Glumačka postava: %s
Epizoda %d će izaći za
@@ -285,7 +276,6 @@
Rastegni
Zoom
Pravna obavijest
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
Općenito
Gumb za slučajni odabir
Prikaži gumb za slučajni odabir na početnoj stranici i biblioteci
@@ -597,8 +587,6 @@
Prikaži gumb za prebacivanje orijentacije zaslona
Omogućuje automatsko mijenjanje orijentacije zaslona na temelju orijentacije videa
Automatsko rotiranje
- rotiraj_video_tipka
- automatski_rotiraj_video_tipka
Obavijest za novu epizodu
Traži u drugim proširenjima
Dodaje opciju za brzinu u playeru
@@ -636,7 +624,6 @@
CloudStream Wiki
Računi
Sigurnost
- hide_player_control_names_key
Lokalna autentifikacija
Otvori lokalni video
Posjeti %s na svom mobitelu ili računalu i unesi gore navedeni kod
diff --git a/app/src/main/res/values-b+hu/strings.xml b/app/src/main/res/values-b+hu/strings.xml
index 1e97719c0..ae018207b 100644
--- a/app/src/main/res/values-b+hu/strings.xml
+++ b/app/src/main/res/values-b+hu/strings.xml
@@ -579,7 +579,6 @@
A PIN 4 karakter hosszú kell legyen
Auto elforgatás
Az automatikus videó orientáció alapján való képernyő elforgatás bekapcsolása
- hide_player_control_names_key
Helyi videó megnyitása
Tárhely név és URL
Ez a videó egy Torrent, ami azt jelenti, hogy a videótevékenységed nyomon követhető.\nGyőződj meg róla, hogy megérted a torrentezés működését, mielőtt folytatnád.
diff --git a/app/src/main/res/values-b+in/strings.xml b/app/src/main/res/values-b+in/strings.xml
index ca3a39906..17b4f075d 100644
--- a/app/src/main/res/values-b+in/strings.xml
+++ b/app/src/main/res/values-b+in/strings.xml
@@ -250,7 +250,6 @@
Regang
Zoom
Disclaimer
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
Umum
Tombol Acak
Tampilkan tombol acak di Beranda dan Pustaka
@@ -267,14 +266,6 @@
Lokasi judul poster
Meletakkan judul di bawah poster
-
%1$s %2$s
akun
Keluar
@@ -632,7 +623,6 @@
CloudStream Wiki
Keamanan
Akun
- hide_player_control_names_key
Hati hati
Kode PIN kini telah kedaluwarsa!
Gambar Kode QR
diff --git a/app/src/main/res/values-b+it/strings.xml b/app/src/main/res/values-b+it/strings.xml
index fee5b56f3..3962f5d23 100644
--- a/app/src/main/res/values-b+it/strings.xml
+++ b/app/src/main/res/values-b+it/strings.xml
@@ -640,7 +640,6 @@
Impossibile ottenere il codice PIN del dispositivo, prova l\'autenticazione locale
Il codice PIN è scaduto!
Il codice scadrà tra %1$dm %2$ds
- hide_player_control_names_key
Apri il video locale
Al momento non ci sono download.
Riproduci dall\'inizio
diff --git a/app/src/main/res/values-b+iw/strings.xml b/app/src/main/res/values-b+iw/strings.xml
index 558d98a4b..ef4cb9202 100644
--- a/app/src/main/res/values-b+iw/strings.xml
+++ b/app/src/main/res/values-b+iw/strings.xml
@@ -537,7 +537,6 @@
\nיגרמו לעדיפות הסרטון להיות 10.
\n
\nשימו לב: אם הסכום הוא 10 או יותר, הנגן ידלג על טעינת הסרטון כאשר הלינק נטען!
- hide_player_control_names_key
עונה %1$d פרק %2$d תשודר ב:
%1$d שעות %2$d דקות %3$d שניות
%1$d דקות %2$d שניות
diff --git a/app/src/main/res/values-b+ja/strings.xml b/app/src/main/res/values-b+ja/strings.xml
index df0559ef7..e246c6f27 100644
--- a/app/src/main/res/values-b+ja/strings.xml
+++ b/app/src/main/res/values-b+ja/strings.xml
@@ -234,7 +234,6 @@
現在のエピソードが終了したら次のエピソードを開始する
長押しするとデフォルトにリセットされます
ダウンロードを再開
- hide_player_control_names_key
ブックマークのフィルタ
プロットが見つかりません
ダブルタップで一時停止
diff --git a/app/src/main/res/values-b+kn/strings.xml b/app/src/main/res/values-b+kn/strings.xml
index 3a77aeef0..22a45b906 100644
--- a/app/src/main/res/values-b+kn/strings.xml
+++ b/app/src/main/res/values-b+kn/strings.xml
@@ -129,5 +129,4 @@
Brightness ಅಥವಾ volume ಬದಲಾಯಿಸಲು ಎಡ ಅಥವಾ ಬಲಭಾಗದಲ್ಲಿ ಮೇಲಕ್ಕೆ ಅಥವಾ ಕೆಳಕ್ಕೆ ಸ್ಲೈಡ್ ಮಾಡಿ
ಈಗಿನ ಎಪಿಸೋಡ್ ಮುಗಿದಾಗ ಮುಂದಿನ ಎಪಿಸೋಡ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಿ
ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಸ್ವೈಪ್ ಮಾಡಿ
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+ko/strings.xml b/app/src/main/res/values-b+ko/strings.xml
index 7ab550913..af84eb3a9 100644
--- a/app/src/main/res/values-b+ko/strings.xml
+++ b/app/src/main/res/values-b+ko/strings.xml
@@ -620,7 +620,6 @@
%s의 PIN 입력
즐겨찾기에서 제거
캐스트미러
- hide_player_control_names_key
플러그인 삭제
경고
탐색바 미리보기
diff --git a/app/src/main/res/values-b+lt/strings.xml b/app/src/main/res/values-b+lt/strings.xml
index 357192018..cb2d816f3 100644
--- a/app/src/main/res/values-b+lt/strings.xml
+++ b/app/src/main/res/values-b+lt/strings.xml
@@ -254,5 +254,4 @@
Ar tikrai norite išeiti?
Pašalinti iš žiūrimų
Garso takelis
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+lv/strings.xml b/app/src/main/res/values-b+lv/strings.xml
index 444a59a5f..b87e9e4fe 100644
--- a/app/src/main/res/values-b+lv/strings.xml
+++ b/app/src/main/res/values-b+lv/strings.xml
@@ -509,7 +509,6 @@
Abonēto šovu atjaunināšana
Abonēts
Abonēts %s
- hide_player_control_names_key
%1$d. sezona un %2$d. sērija tiks izlaista pēc
%1$dh %2$dm %3$ds
%1$dm %2$ds
diff --git a/app/src/main/res/values-b+mk/strings.xml b/app/src/main/res/values-b+mk/strings.xml
index bccc2a00d..6998c49db 100644
--- a/app/src/main/res/values-b+mk/strings.xml
+++ b/app/src/main/res/values-b+mk/strings.xml
@@ -593,7 +593,6 @@
Грешка при пристапот до таблата со исечоци, обиди се повторно.
Грешка при копирање, молам копирај го логот и контактирај ја поддршката на апликацијата.
Аудио книга
- hide_player_control_names_key
Безбедност
Отфрли
Отвори извор
diff --git a/app/src/main/res/values-b+ml/strings.xml b/app/src/main/res/values-b+ml/strings.xml
index dcb9e5270..d1c9409a3 100644
--- a/app/src/main/res/values-b+ml/strings.xml
+++ b/app/src/main/res/values-b+ml/strings.xml
@@ -272,5 +272,4 @@
എഡ്ജ് തരം
ഔട്ട്ലൈൻ നിറം
പശ്ചാത്തല നിറം
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+ms/strings.xml b/app/src/main/res/values-b+ms/strings.xml
index 8bbb2a7e0..83492a5ff 100644
--- a/app/src/main/res/values-b+ms/strings.xml
+++ b/app/src/main/res/values-b+ms/strings.xml
@@ -54,7 +54,6 @@
Kongsi
Tetapan
Tutup
- hide_player_control_names_key
Pratonton
Resensi:%.1f
Kemas kini baru dijumpai!
@@ -516,9 +515,6 @@
Sandarkan data
Gagal pulihkan data dari fail %s
Ralat sandaran %s
-
- - Episode
-
Akan datang pada %s
Ini akan memadamkan secara kekal %s\nAdakah anda pasti?
Adakah anda pasti mahu memadamkan item berikut secara kekal?\n\n%s
diff --git a/app/src/main/res/values-b+mt/strings.xml b/app/src/main/res/values-b+mt/strings.xml
index ca62a043b..ea859ee29 100644
--- a/app/src/main/res/values-b+mt/strings.xml
+++ b/app/src/main/res/values-b+mt/strings.xml
@@ -122,5 +122,4 @@
Bookmarks
Neħħi
Falla t-tniżżil
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+my/strings.xml b/app/src/main/res/values-b+my/strings.xml
index 1d35cbaa0..4a7a50aa7 100644
--- a/app/src/main/res/values-b+my/strings.xml
+++ b/app/src/main/res/values-b+my/strings.xml
@@ -537,5 +537,4 @@
သင်နဂိုတည်းကသတ်မှတ်ပြီး
လိုက်ဘရီရွေးချယ်ရန်
ဖြင့်ဖွင့်မည်
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+ne/strings.xml b/app/src/main/res/values-b+ne/strings.xml
index 9345cab2b..8a432a505 100644
--- a/app/src/main/res/values-b+ne/strings.xml
+++ b/app/src/main/res/values-b+ne/strings.xml
@@ -127,5 +127,4 @@
प्लेयरको उपशीर्षकको सेटिङ
रिपोजिटरी को नाम र यूआरएल
कपी गरियो!
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+nl/strings.xml b/app/src/main/res/values-b+nl/strings.xml
index 6e0982c79..54508e652 100644
--- a/app/src/main/res/values-b+nl/strings.xml
+++ b/app/src/main/res/values-b+nl/strings.xml
@@ -292,14 +292,6 @@
MyCoolSite
voorbeeld.com
Taalcode (nl)
-
%1$s %2$s
account
Log uit
@@ -594,7 +586,6 @@
Link opnieuw geladen
Autoroteer
Roteer
- hide_player_control_names_key
Er zijn momenteel geen downloads beschikbaar.
Gekopieerd!
Verbergen
diff --git a/app/src/main/res/values-b+nn/strings.xml b/app/src/main/res/values-b+nn/strings.xml
index 2cf83c183..245bf6618 100644
--- a/app/src/main/res/values-b+nn/strings.xml
+++ b/app/src/main/res/values-b+nn/strings.xml
@@ -191,5 +191,4 @@
Bilde i bilde
Fortsett å sjå
Prøv tilkopling på nytt…
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+no/strings.xml b/app/src/main/res/values-b+no/strings.xml
index a981609cf..374b033c6 100644
--- a/app/src/main/res/values-b+no/strings.xml
+++ b/app/src/main/res/values-b+no/strings.xml
@@ -192,8 +192,6 @@
Primær Farge
App Tema
Foretrukket Videoinnhold
- Disclaimer
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
Besetning: %s
%dm
Tøm
@@ -525,5 +523,4 @@
Bruk
Hjelp
Profilbakgrunn
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+or/strings.xml b/app/src/main/res/values-b+or/strings.xml
index 807a3bcc6..8c9379f5b 100644
--- a/app/src/main/res/values-b+or/strings.xml
+++ b/app/src/main/res/values-b+or/strings.xml
@@ -155,7 +155,6 @@
କୌଣସି ତଥ୍ୟ ନାହିଁ
%1$s ଅ %2$d
ଆଦ୍ୟ ବାଦ୍ ଦିଅ
- hide_player_control_names_key
ଏପିସୋଡ୍ %d ମୁକ୍ତିଲାଭ କରିବ
ସିଜିନ୍ %1$d ଏପିସୋଡ୍ %2$d ମୁକ୍ତିଲାଭ କରିବ
%1$dଘଣ୍ଟା %2$dମିନିଟ୍ %3$dସେକେଣ୍ଡ
diff --git a/app/src/main/res/values-b+pl/array.xml b/app/src/main/res/values-b+pl/array.xml
index 7b1683b41..466066852 100644
--- a/app/src/main/res/values-b+pl/array.xml
+++ b/app/src/main/res/values-b+pl/array.xml
@@ -299,48 +299,4 @@
- Vietnamese (VISCII)
- Vietnamese (Windows-1258)
-
-
- - UTF-8
- - UTF-16
- - UTF-16BE
- - UTF-16LE
- - GB18030
- - ISO-8859-15
- - Windows-1252
- - IBM850
- - ISO-8859-2
- - Windows-1250
- - ISO-8859-3
- - ISO-8859-10
- - Windows-1251
- - KOI8-R
- - KOI8-U
- - ISO-8859-6
- - Windows-1256
- - ISO-8859-7
- - Windows-1253
- - ISO-8859-8
- - Windows-1255
- - ISO-8859-9
- - Windows-1254
- - ISO-8859-11
- - Windows-874
- - ISO-8859-13
- - Windows-1257
- - ISO-8859-14
- - ISO-8859-16
- - ISO-2022-CN-EXT
- - EUC-CN
- - ISO-2022-JP-2
- - EUC-JP
- - Shift_JIS
- - CP949
- - ISO-2022-KR
- - Big5
- - ISO-2022-TW
- - Big5-HKSCS
- - VISCII
- - Windows-1258
-
diff --git a/app/src/main/res/values-b+pl/strings.xml b/app/src/main/res/values-b+pl/strings.xml
index e067b391c..59ac0db4b 100644
--- a/app/src/main/res/values-b+pl/strings.xml
+++ b/app/src/main/res/values-b+pl/strings.xml
@@ -621,7 +621,6 @@
Odrzuć
Otwórz repozytorium
Odwiedź %s na swoim smartfonie lub komputerze i wprowadź powyższy kod
- hide_player_control_names_key
Odtwarzaj od początku
Usuń wtyczkę
Uwaga
diff --git a/app/src/main/res/values-b+pt+BR/strings.xml b/app/src/main/res/values-b+pt+BR/strings.xml
index 3fbc4fb28..e74a7db74 100644
--- a/app/src/main/res/values-b+pt+BR/strings.xml
+++ b/app/src/main/res/values-b+pt+BR/strings.xml
@@ -266,7 +266,6 @@
Esticar
Zoom
Aviso Legal
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
Geral
Botão Aleatório
Mostrar botão aleatório na página inicial e na biblioteca
@@ -291,14 +290,6 @@
NovoNomedoSite
https://example.com
Codigo da Língua (bp)
-
%1$s %2$s
Conta
Sair
@@ -640,7 +631,6 @@
Não é possível obter o código PIN do dispositivo, tente a autenticação local
O código PIN expirou!
O código expira em %1$dm %2$ds
- hide_player_control_names_key
Reproduzir do começo
Reprovou alguns testes
Excluir plugin
diff --git a/app/src/main/res/values-b+pt/strings.xml b/app/src/main/res/values-b+pt/strings.xml
index 23b49195f..e7b3623e6 100644
--- a/app/src/main/res/values-b+pt/strings.xml
+++ b/app/src/main/res/values-b+pt/strings.xml
@@ -606,7 +606,6 @@
Temporada %1$d Episódio %2$d será lançado em
Escolha o dispositivo
Transmitir
- hide_player_control_names_key
Abrir vídeo local
Não é possível receber o PIN do dispositivo, tentando autenticação local
Pré-visualização na barra de progresso
diff --git a/app/src/main/res/values-b+qt/strings.xml b/app/src/main/res/values-b+qt/strings.xml
index 3de0f32df..d60a4e32c 100644
--- a/app/src/main/res/values-b+qt/strings.xml
+++ b/app/src/main/res/values-b+qt/strings.xml
@@ -239,7 +239,6 @@
oooooh uuaagh
@string/home_play
oouuhhh ahhooo-ahah
- hide_player_control_names_key
uuuugg aaaahh oogg
aagg uuuuggg og %1$dm %2$ds
aaaahhh ag
diff --git a/app/src/main/res/values-b+ro/strings.xml b/app/src/main/res/values-b+ro/strings.xml
index 642eea0c3..e2dbd0e32 100644
--- a/app/src/main/res/values-b+ro/strings.xml
+++ b/app/src/main/res/values-b+ro/strings.xml
@@ -261,7 +261,6 @@
Întindere
Mărire
Aviz juridic (declinarea responsabilității și drepturi de autor)
- Orice probleme legale privind conținutul acestei aplicații ar trebui să fie rezolvate cu furnizorii și gazdele actuale de fișiere, întrucât noi nu suntem afiliați cu aceștia. În caz de încălcare a drepturilor de autor, vă rugăm să contactați direct părțile responsabile sau site-urile de streaming. Aplicația este destinată exclusiv utilizării educaționale și personale. CloudStream 3 nu găzduiește niciun fel de conținut în aplicație și nu are niciun control asupra conținutului media care este pus sau retras. CloudStream 3 funcționează ca orice alt motor de căutare, cum ar fi Google. CloudStream 3 nu găzduiește, nu încarcă și nu gestionează niciun videoclip, film sau conținut. Pur și simplu navighează, adună și afișează linkuri într-o interfață convenabilă și ușor de utilizat. Pur și simplu, acesta extrage paginile web ale unor terțe părți care sunt accesibile publicului prin intermediul oricărui browser web obișnuit. Este responsabilitatea utilizatorului de a evita orice acțiune care ar putea încălca legile care guvernează locația sa. Utilizați CloudStream 3 pe propria răspundere.
General
Aleatoriu
Afișează butonul pentru aleatoriu pe Pagina Principală și în Bibliotecă
@@ -627,7 +626,6 @@
Sezonul %1$d Episod %2$d va fi lansat în
Selectați divece-ul pe care doriți să faceți cast
Cast mirror
- hide_player_control_names_key
Redă de la început
Nu există descărcări.
Selectionati elementele de sters
diff --git a/app/src/main/res/values-b+ru/strings.xml b/app/src/main/res/values-b+ru/strings.xml
index d25f7e274..d8b369a3d 100644
--- a/app/src/main/res/values-b+ru/strings.xml
+++ b/app/src/main/res/values-b+ru/strings.xml
@@ -555,12 +555,10 @@
Добавить в любимое
Включить автоматическую смену ориентации экрана на основе ориентации видео
Автоповорот
- rotate_video_key
Использовать учётную запись по умолчанию
Отписаться
Заменить
Введите текущий ПИН-код
- auto_rotate_video_key
Любимые
%s добавлено в любимые
Введите ПИН-код от %s
@@ -600,7 +598,6 @@
Сезон %1$d серия %2$d выйдет
Выйдет %s
Выберите устройство для трансляции
- hide_player_control_names_key
В данный момент скачиваний нет.
Играть с самого начала
Открыть локальное видео
diff --git a/app/src/main/res/values-b+sk/strings.xml b/app/src/main/res/values-b+sk/strings.xml
index fb65841f2..93505971c 100644
--- a/app/src/main/res/values-b+sk/strings.xml
+++ b/app/src/main/res/values-b+sk/strings.xml
@@ -369,7 +369,6 @@
Pridať repozitár
Názov repozitára
Zobraziť komunitné repozitáre
- hide_player_control_names_key
HD
Prehrávač
Rozlíšenie a titul
diff --git a/app/src/main/res/values-b+so/strings.xml b/app/src/main/res/values-b+so/strings.xml
index fc42c63f7..09499af00 100644
--- a/app/src/main/res/values-b+so/strings.xml
+++ b/app/src/main/res/values-b+so/strings.xml
@@ -472,5 +472,4 @@
Bilowga
Bilow isku qasan
Qoraalka dhamaadka
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+sv/strings.xml b/app/src/main/res/values-b+sv/strings.xml
index dfbfce4b5..75c7efda4 100644
--- a/app/src/main/res/values-b+sv/strings.xml
+++ b/app/src/main/res/values-b+sv/strings.xml
@@ -465,7 +465,6 @@
Redigera konto
Loggat in som %s
Hoppa över val av konto vid start
- auto_rotera_video_nyckel
Gå förbi blockering av rå GitHub-URL:er med jsDelivr. Kan göra att uppdateringar försenas med några dagar.
Funktion
Önskad media
@@ -557,7 +556,6 @@
%s togs bort från favoriter
%s har lagts till i favoriter
Använd standard konto
- rotera_video_nyckel
PIN-kod
Sök mängden som används när spelaren är dold
Det verkar som om ett potentiellt duplicerat objekt redan finns i ditt bibliotek:
@@ -612,7 +610,6 @@
CloudStream Wiki
Konton
Säkerhet
- hide_player_control_names_key
Avfärda
Öppna databasen
Koden löper ut om %1$dm %2$ds
diff --git a/app/src/main/res/values-b+ta/strings.xml b/app/src/main/res/values-b+ta/strings.xml
index 94b6f717a..e223f6c60 100644
--- a/app/src/main/res/values-b+ta/strings.xml
+++ b/app/src/main/res/values-b+ta/strings.xml
@@ -576,7 +576,6 @@
ஆதாரங்கள் எவ்வாறு உத்தரவிடப்படுகின்றன என்பதை இங்கே மாற்றலாம். ஒரு வீடியோவுக்கு அதிக முன்னுரிமை இருந்தால், அது மூல தேர்வில் அதிகமாகத் தோன்றும். மூல முன்னுரிமையின் தொகை மற்றும் தரமான முன்னுரிமை ஆகியவை வீடியோ முன்னுரிமை. \n\n சான்று A: 3 \n தகுதி பி: 7 \n 10 இன் ஒருங்கிணைந்த வீடியோ முன்னுரிமை இருக்கும். \n\n குறிப்பு: தொகை 10 அல்லது அதற்கு மேற்பட்டதாக இருந்தால், அந்த இணைப்பு ஏற்றப்படும்போது பிளேயர் தானாகவே ஏற்றுவதைத் தவிர்க்கும்!
உங்கள் கிளவுட்ச்ட்ரீம் தரவு இப்போது காப்புப் பிரதி எடுக்கப்பட்டுள்ளது. இதன் சாத்தியம் மிகக் குறைவு என்றாலும், எல்லா சாதனங்களும் வித்தியாசமாக நடந்து கொள்ளலாம். அரிய விசயத்தில், பயன்பாட்டை அணுகுவதிலிருந்து நீங்கள் பூட்டப்படுகிறீர்கள், பயன்பாட்டு தரவை முழுவதுமாக அழித்து, காப்புப்பிரதியிலிருந்து மீட்டெடுக்கவும். இதிலிருந்து எழும் ஏதேனும் சிரமத்திற்கு நாங்கள் மிகவும் வருந்துகிறோம்.
ஊடகம்
- hide_player_control_names_key
கணக்குகள்
எச்சரிக்கை
தற்போது பதிவிறக்கங்கள் எதுவும் இல்லை.
diff --git a/app/src/main/res/values-b+ti/strings.xml b/app/src/main/res/values-b+ti/strings.xml
index 6c154c8d8..46235bbd7 100644
--- a/app/src/main/res/values-b+ti/strings.xml
+++ b/app/src/main/res/values-b+ti/strings.xml
@@ -3,5 +3,4 @@
%1$s ክፋል %2$d
ክፋል %d በ ላይ ይወጣል
ተዋሳእቲ፡ %s
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+tl/strings.xml b/app/src/main/res/values-b+tl/strings.xml
index 94bb8ea1d..4050ddbd7 100644
--- a/app/src/main/res/values-b+tl/strings.xml
+++ b/app/src/main/res/values-b+tl/strings.xml
@@ -257,5 +257,4 @@
Mga Subtitle ng Chromecast
Mga setting ng mga subtitle ng Chromecast
Maglaro ng Trailer
- hide_player_control_names_key
diff --git a/app/src/main/res/values-b+tr/array.xml b/app/src/main/res/values-b+tr/array.xml
index 56cea4c9c..dbc2d3e66 100644
--- a/app/src/main/res/values-b+tr/array.xml
+++ b/app/src/main/res/values-b+tr/array.xml
@@ -319,48 +319,4 @@
- Vietnamese (VISCII)
- Vietnamese (Windows-1258)
-
-
- - UTF-8
- - UTF-16
- - UTF-16BE
- - UTF-16LE
- - GB18030
- - ISO-8859-15
- - Windows-1252
- - IBM850
- - ISO-8859-2
- - Windows-1250
- - ISO-8859-3
- - ISO-8859-10
- - Windows-1251
- - KOI8-R
- - KOI8-U
- - ISO-8859-6
- - Windows-1256
- - ISO-8859-7
- - Windows-1253
- - ISO-8859-8
- - Windows-1255
- - ISO-8859-9
- - Windows-1254
- - ISO-8859-11
- - Windows-874
- - ISO-8859-13
- - Windows-1257
- - ISO-8859-14
- - ISO-8859-16
- - ISO-2022-CN-EXT
- - EUC-CN
- - ISO-2022-JP-2
- - EUC-JP
- - Shift_JIS
- - CP949
- - ISO-2022-KR
- - Big5
- - ISO-2022-TW
- - Big5-HKSCS
- - VISCII
- - Windows-1258
-
diff --git a/app/src/main/res/values-b+tr/strings.xml b/app/src/main/res/values-b+tr/strings.xml
index f6a125b91..10137e7b5 100644
--- a/app/src/main/res/values-b+tr/strings.xml
+++ b/app/src/main/res/values-b+tr/strings.xml
@@ -1,15 +1,6 @@
- %d %s | %s
- %s • %s
- %s / %s
- %s %s
- +%d
- -%d
- %d
- %d
- %d
%1$s B. %2$d
Cast: %s
Bölüm %d şu tarihte yayınlanacak
@@ -22,9 +13,7 @@
Bölüm Afişi
Ana Afiş
Sonraki Rastgele
- @string/play_episode
Geri git
- @string/home_change_provider_img_des
Sağlayıcıyı Değiştir
Arkaplanı Önizle
@@ -45,7 +34,6 @@
Veri Yok
Daha Fazla Seçenek
Sonraki bölüm
- @string/synopsis
Türler
Paylaş
Tarayıcıda aç
@@ -74,7 +62,6 @@
İndirme Başarısız
İndirme İptal Edildi
İndirme Tamamlandı
- %s - %s
Ağ akışı
Bağlantılar yüklenirken hata oluştu
Dahili Depolama
@@ -258,10 +245,6 @@
Dublaj etiketi
Altyazı etiketi
Başlık
- show_hd_key
- show_dub_key
- show_sub_key
- show_title_key
Afiş üzerindeki öğeleri değiştir
Güncelleme bulunamadı
Güncellemeleri denetle
@@ -293,8 +276,6 @@
Uzat
Yakınlaştır
Yasal uyarı
- legal_notice_key
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
Genel
Rastgele düğmesi
Ana sayfa ve kütüphane üstünde rastgele düğmesini göster
@@ -314,10 +295,6 @@
Afiş başlık konumu
Başlığı afişin altına yerleştir
- anilist_key
- mal_key
- opensubtitles_key
- nginx_key
şifre123
Kullanıcı Adı
hello@world.com
@@ -325,14 +302,6 @@
YeniSiteAdı
https://ornek.com
Dil kodu (tr)
-
%1$s %2$s
hesap
Çıkış yap
@@ -355,7 +324,6 @@
Tümü
Azami
Asgari
- @string/none
Dış hat
Çökmüş
Gölge
@@ -614,8 +582,6 @@
PIN
Geçerli PIN\'i Giriniz
Ekran yönü için bir geçiş düğmesi göster
- rotate_video_key
- auto_rotate_video_key
Otomatik döndür
Döndür
Video yönüne göre ekran yönünün otomatik olarak değişmesini sağla
@@ -664,7 +630,6 @@
Cihaz PIN kodu alınamıyor, yerel kimlik doğrulamayı deneyin
PIN kodunun süresi doldu!
Kodun süresi %1$dm %2$ds içinde doluyor
- hide_player_control_names_key
Şu an hiç bir indirme bulunmamaktadır.
Eklentiyi sil
Yerel videoyu aç
diff --git a/app/src/main/res/values-b+uk/strings.xml b/app/src/main/res/values-b+uk/strings.xml
index 54562f263..20bd9e6e2 100644
--- a/app/src/main/res/values-b+uk/strings.xml
+++ b/app/src/main/res/values-b+uk/strings.xml
@@ -550,10 +550,8 @@
Керувати обліковими записами
Редагувати обліковий запис
Показувати кнопку перемикання орієнтації екрана
- rotate_video_key
Обернути
Покликання перезавантажено
- auto_rotate_video_key
Автообертання
Увімкнути автоматичну зміну орієнтації екрана відповідно до відео
Додати налаштування швидкості до програвача
@@ -600,7 +598,6 @@
Термін дії коду закінчується через %1$dхв %2$dс
Локальна автентифікація
Відхилити
- hide_player_control_names_key
Відтворити з початку
Попередження
Видалити розширення
diff --git a/app/src/main/res/values-b+ur/strings.xml b/app/src/main/res/values-b+ur/strings.xml
index d2c3d9f1c..5f6d8aa14 100644
--- a/app/src/main/res/values-b+ur/strings.xml
+++ b/app/src/main/res/values-b+ur/strings.xml
@@ -604,7 +604,6 @@
دیگر ایکسٹینشنز میں تلاش کریں
سفارشات دکھائیں
آپ کے CloudStream ڈیٹا کا اب بیک اپ لیا گیا ہے۔ اگرچہ اس کا امکان بہت کم ہے، لیکن مختلف ڈیوائس مختلف طریقے سے کام کر سکتے ہیں۔ اگر آپ ایپ تک رسائی حاصل کرنے سے قاصر ہیں تو، ایپ کا ڈیٹا مکمل طور پر صاف کریں اور بیک اپ سے بحال کریں۔ اس سے ہونے والی کسی بھی تکلیف کے لیے ہم بہت معذرت خواہ ہیں۔
- hide_player_control_names_key
سیزن %1$d کی قسط %2$d جاری ہوگی
شروع سےپلے کریں
کلام شناسی دستیاب نہیں
diff --git a/app/src/main/res/values-b+vi/array.xml b/app/src/main/res/values-b+vi/array.xml
index d1887505e..16d45e516 100644
--- a/app/src/main/res/values-b+vi/array.xml
+++ b/app/src/main/res/values-b+vi/array.xml
@@ -291,48 +291,4 @@
- Vietnamese (VISCII)
- Vietnamese (Windows-1258)
-
-
- - UTF-8
- - UTF-16
- - UTF-16BE
- - UTF-16LE
- - GB18030
- - ISO-8859-15
- - Windows-1252
- - IBM850
- - ISO-8859-2
- - Windows-1250
- - ISO-8859-3
- - ISO-8859-10
- - Windows-1251
- - KOI8-R
- - KOI8-U
- - ISO-8859-6
- - Windows-1256
- - ISO-8859-7
- - Windows-1253
- - ISO-8859-8
- - Windows-1255
- - ISO-8859-9
- - Windows-1254
- - ISO-8859-11
- - Windows-874
- - ISO-8859-13
- - Windows-1257
- - ISO-8859-14
- - ISO-8859-16
- - ISO-2022-CN-EXT
- - EUC-CN
- - ISO-2022-JP-2
- - EUC-JP
- - Shift_JIS
- - CP949
- - ISO-2022-KR
- - Big5
- - ISO-2022-TW
- - Big5-HKSCS
- - VISCII
- - Windows-1258
-
diff --git a/app/src/main/res/values-b+vi/strings.xml b/app/src/main/res/values-b+vi/strings.xml
index aa9caffd1..b26c715f3 100644
--- a/app/src/main/res/values-b+vi/strings.xml
+++ b/app/src/main/res/values-b+vi/strings.xml
@@ -630,7 +630,6 @@
Truy cập %s trên điện thoại hoặc máy tính và nhập mã bên trên
Mã PIN đã hết hạn!
Mã sẽ hết hạn trong %1$dm %2$ds
- hide_player_control_names_key
Không lấy được mã PIN, hãy thử xác thực cục bộ
Hiện không có bản tải xuống nào.
Xác thực cục bộ
diff --git a/app/src/main/res/values-b+zh+TW/strings.xml b/app/src/main/res/values-b+zh+TW/strings.xml
index 251df543c..7dc4b48f2 100644
--- a/app/src/main/res/values-b+zh+TW/strings.xml
+++ b/app/src/main/res/values-b+zh+TW/strings.xml
@@ -1,15 +1,6 @@
- %d %s | %s
- %s • %s
- %s / %s
- %s %s
- +%d
- -%d
- %d
- %d
- %d
%1$s 共 %2$d 集
演員:%s
第 %d 集即將發佈於
@@ -22,9 +13,7 @@
劇集封面
主封面
隨機下一個
- @string/play_episode
返回
- @string/home_change_provider_img_des
更改片源
預覽背景
@@ -45,7 +34,6 @@
無資料
更多選項
下一集
- @string/synopsis
類型
分享
在瀏覽器中打開
@@ -74,7 +62,6 @@
下載失敗
下載取消
下載完畢
- %s - %s
網路串流
載入連結錯誤
內部儲存空間
@@ -259,10 +246,6 @@
配音標籤
字幕標籤
標題
- show_hd_key
- show_dub_key
- show_sub_key
- show_title_key
封面內容
未找到更新
檢查更新
@@ -294,8 +277,6 @@
拉伸
縮放
免責聲明
- legal_notice_key
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
通用
隨機按鈕
在主畫面與媒體庫中顯示隨機按鈕
@@ -315,10 +296,6 @@
封面標題位置
將標題移到封面下方
- anilist_key
- mal_key
- opensubtitles_key
- nginx_key
密碼
使用者名稱
電子郵件
@@ -326,14 +303,6 @@
新網站名稱
https://example.com
語言代號 (zh_TW)
-
%1$s %2$s
帳號
登出
@@ -356,7 +325,6 @@
全部
最大
最小
- @string/none
輪廓
凹陷
陰影
@@ -614,9 +582,7 @@
\n注意:如果加總達到 10 或更高,則載入該連結時播放器將自動跳過載入!
輸入目前的 PIN 碼
顯示切換畫面方向的按鈕
- rotate_video_key
選擇篩選外掛程式下載的模式
- auto_rotate_video_key
自動旋轉
旋轉
根據影片方向自動切換畫面方向
@@ -656,7 +622,6 @@
為了確保下載與通知已訂閱的電視節目的不間斷,CloudStream 需要取得在背景執行的權限。若點選「確定」,將移至「應用程式資訊」,請找到「應用程式電池使用」並將電池用量設置為「無限制」。請注意,取得此權限並不表示 CS3 會明顯增加電池用量,而是只在必要時在背景執行,例如取得通知或使用官方擴充功能下載影片時。若選擇「取消」,您可以稍後在「一般設定」中調整此設定。
CloudStream Wiki
此裝置不支援生物特徵認證
- hide_player_control_names_key
無法取得裝置 PIN 碼,請嘗試本機驗證
刪除外掛程式
開啟資源庫
diff --git a/app/src/main/res/values-b+zh/strings.xml b/app/src/main/res/values-b+zh/strings.xml
index 224041e49..0301a3a2d 100644
--- a/app/src/main/res/values-b+zh/strings.xml
+++ b/app/src/main/res/values-b+zh/strings.xml
@@ -1,15 +1,6 @@
- %d %s | %s
- %s • %s
- %s / %s
- %s %s
- +%d
- -%d
- %d
- %d
- %d
%1$s 共 %2$d 集
演员:%s
第 %d 集将发布于
@@ -22,9 +13,7 @@
剧集封面
主封面
随机下一个
- @string/play_episode
返回
- @string/home_change_provider_img_des
更改片源
预览背景
@@ -45,7 +34,6 @@
无数据
更多选项
下一集
- @string/synopsis
类型
分享
在浏览器中打开
@@ -74,7 +62,6 @@
下载失败
下载取消
下载完毕
- %s - %s
播放
加载链接错误
内部存储
@@ -260,10 +247,6 @@
配音标签
字幕标签
标题
- show_hd_key
- show_dub_key
- show_sub_key
- show_title_key
封面内容
未找到更新
检查更新
@@ -295,8 +278,6 @@
拉伸
缩放
免责声明
- legal_notice_key
- Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk.
通用
随机按钮
在主页和库中显示随机按钮
@@ -316,10 +297,6 @@
封面标题位置
将标题移至封面下方
- anilist_key
- mal_key
- opensubtitles_key
- nginx_key
密码
用户名
邮箱
@@ -327,14 +304,6 @@
网站名称
网站链接
语言代码 (zh)
-
%1$s %2$s
账户
注销
@@ -357,7 +326,6 @@
全部
最大
最小
- @string/none
轮廓
凹陷
阴影
@@ -654,7 +622,6 @@
选择投射设备
%1$d季%2$d集将在
投射镜像
- hide_player_control_names_key
目前尚无下载。
打开本地视频
安全
diff --git a/app/src/main/res/values/donottranslate-strings.xml b/app/src/main/res/values/donottranslate-strings.xml
new file mode 100644
index 000000000..5f2186fae
--- /dev/null
+++ b/app/src/main/res/values/donottranslate-strings.xml
@@ -0,0 +1,152 @@
+
+
+
+ search_providers_list
+ app_locale
+ search_type_list
+ auto_update
+ auto_update_plugins
+ auto_download_plugins_key2
+ skip_update_key
+ install_prerelease_key
+ manual_check_update
+ fast_forward_button_time
+ benene_count
+ subtitle_settings_key
+ test_providers_key
+ subtitle_settings_chromecast_key
+ quality_pref_key
+ quality_pref_mobile_data_key
+ player_default_key
+ prefer_limit_title_key
+ prefer_limit_title_rez_key
+ apk_installer_key
+ video_buffer_size_key
+ video_buffer_length_key
+ video_buffer_clear_key
+ video_buffer_disk_key
+ use_system_brightness_key
+ swipe_enabled_key
+ playback_speed_enabled_key
+ player_resize_enabled_key
+ pip_enabled_key
+ double_tap_enabled_key
+ double_tap_pause_enabled_key
+ double_tap_seek_time_key2
+ android_tv_interface_off_seek_key
+ android_tv_interface_on_seek_key
+ swipe_vertical_enabled_key
+ autoplay_next_key
+ display_sub_key
+ show_fillers_key
+ show_trailers_key
+ show_kitsu_posters_key
+ random_button_key
+ provider_lang_key
+ dns_key
+ jsdelivr_proxy_key
+ download_path_key
+ download_parallel_key
+ download_concurrent_key
+ download_path_key_visual
+ Cloudstream
+ app_layout_key
+ primary_color_key
+ restore_key
+ backup_key
+ automatic_backup_key
+ prefer_media_type_key_2
+ app_theme_key
+ episode_sync_enabled_key
+ log_enabled_key
+ show_logcat_key
+ bottom_title_key
+ poster_ui_key
+ overscan_key
+ poster_size_key
+ subtitles_encoding_key
+ override_site_key
+ redo_setup_key
+ filter_sub_lang_key
+ pref_filter_search_quality_key
+ enable_nsfw_on_providers_key
+ skip_startup_account_select_key
+ enable_skip_op_from_database
+ rotate_video_key
+ auto_rotate_video_key
+ biometric_key
+ battery_optimisation
+ show_hd_key
+ show_dub_key
+ show_sub_key
+ show_rating_key
+ show_title_key
+ show_episode_text_key
+ hide_player_control_names_key
+ preview_seekbar_key
+ backup_path_key
+ backup_dir_key
+ confirm_exit_key
+ software_decoding_key2
+ manual_update_plugins
+ legal_notice_key
+ speedup_key
+ anilist_key
+ simkl_key
+ mal_key
+ opensubtitles_key
+ subdl_key
+
+ pref_category_security_key
+ pref_category_gestures_key
+ pref_category_android_tv_key
+
+ tv_no_focus_tag
+
+
+ %1$d %2$s | %3$s
+ %1$s • %2$s
+ %1$s - %2$s
+ %1$s / %2$s
+ %1$s %2$s
+ S1E1
+ +%d
+ -%d
+ %d
+ %d
+ %s/10.0
+ %d
+
+ @string/play_episode
+ @string/home_change_provider_img_des
+ @string/synopsis
+
+ @string/none
+ @string/none
+ @string/cancel
+ @string/cancel
+ @string/action_default
+ @string/action_default
+
+ Any legal issues regarding the content on this application
+ should be taken up with the actual file hosts and providers themselves as we are not affiliated with them.
+
+ In case of copyright infringement, please directly contact the responsible parties or the streaming websites.
+
+ The app is purely for educational and personal use.
+
+ CloudStream does not host any content on the app, and has no control over what media is put up or taken down.
+ CloudStream functions like any other search engine, such as Google. CloudStream does not host, upload or
+ manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient,
+ user-friendly interface.
+
+ It merely scrapes 3rd-party websites that are publicly accessible via any regular web browser. It is the
+ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
+ CloudStream at your own risk.
+
+
+
+ - @string/episode
+ - @string/episodes
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9aa586e8a..8ad0ec423 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,91 +1,6 @@
-
- search_providers_list
- app_locale
- search_type_list
- auto_update
- auto_update_plugins
- auto_download_plugins_key2
- skip_update_key
- install_prerelease_key
- manual_check_update
- fast_forward_button_time
- benene_count
- subtitle_settings_key
- test_providers_key
- subtitle_settings_chromecast_key
- quality_pref_key
- quality_pref_mobile_data_key
- player_default_key
- prefer_limit_title_key
- prefer_limit_title_rez_key
- apk_installer_key
- video_buffer_size_key
- video_buffer_length_key
- video_buffer_clear_key
- video_buffer_disk_key
- use_system_brightness_key
- swipe_enabled_key
- playback_speed_enabled_key
- player_resize_enabled_key
- pip_enabled_key
- double_tap_enabled_key
- double_tap_pause_enabled_key
- double_tap_seek_time_key2
- android_tv_interface_off_seek_key
- android_tv_interface_on_seek_key
- swipe_vertical_enabled_key
- autoplay_next_key
- display_sub_key
- show_fillers_key
- show_trailers_key
- show_kitsu_posters_key
- random_button_key
- provider_lang_key
- dns_key
- jsdelivr_proxy_key
- download_path_key
- download_parallel_key
- download_concurrent_key
- download_path_key_visual
- Cloudstream
- app_layout_key
- primary_color_key
- restore_key
- backup_key
- automatic_backup_key
- prefer_media_type_key_2
- app_theme_key
- episode_sync_enabled_key
- log_enabled_key
- show_logcat_key
- bottom_title_key
- poster_ui_key
- overscan_key
- poster_size_key
- subtitles_encoding_key
- override_site_key
- redo_setup_key
- filter_sub_lang_key
- pref_filter_search_quality_key
- enable_nsfw_on_providers_key
- skip_startup_account_select_key
- enable_skip_op_from_database
- rotate_video_key
- auto_rotate_video_key
- biometric_key
- %d %s | %s
- %s • %s
- %s / %s
- %s %s
- +%d
- -%d
- %d
- %d
- %s/10.0
- %d
%1$s Ep %2$d
Cast: %s
Episode %d will be released in
@@ -102,10 +17,8 @@
Episode Poster
Main Poster
Next Random
- @string/play_episode
Go back
Play from the Beginning
- @string/home_change_provider_img_des
Change Provider
Preview Background
@@ -127,7 +40,6 @@
No Data
More Options
Next episode
- @string/synopsis
Genres
Share
Open In Browser
@@ -139,7 +51,6 @@
Completed
Dropped
Plan to Watch
- @string/none
Rewatching
Play Movie
Play Trailer
@@ -161,7 +72,6 @@
Download Failed
Download Canceled
Download Done
- %s - %s
Select Items to Delete
There are currently no downloads.
Available for watching offline
@@ -188,7 +98,6 @@
Remove
Set watch status
Apply
- @string/cancel
Copy
Close
Clear
@@ -342,8 +251,6 @@
queued
No Subtitles
Default
- @string/action_default
- @string/action_default
Free
Used
App
@@ -359,10 +266,6 @@
Livestreams
NSFW
Others
-
- - @string/episode
- - @string/episodes
-
Movie
Series
@@ -403,12 +306,6 @@
Rating Label
Title
Episode Text
- show_hd_key
- show_dub_key
- show_sub_key
- show_rating_key
- show_title_key
- show_episode_text_key
Toggle UI elements on poster
No Update Found
Check for Update
@@ -448,23 +345,6 @@
Stretch
Zoom
Disclaimer
- legal_notice_key
- Any legal issues regarding the content on this application
- should be taken up with the actual file hosts and providers themselves as we are not affiliated with them.
-
- In case of copyright infringement, please directly contact the responsible parties or the streaming websites.
-
- The app is purely for educational and personal use.
-
- CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down.
- CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or
- manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient,
- user-friendly interface.
-
- It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the
- responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
- CloudStream 3 at your own risk.
-
ISP Bypasses
Links
App updates
@@ -473,11 +353,8 @@
Actions
Cache
Android TV
- pref_category_android_tv_key
Gestures
- pref_category_gestures_key
Security
- pref_category_security_key
Accounts
Player features
Subtitles
@@ -507,12 +384,6 @@
Poster title location
Put the title under the poster
- anilist_key
- simkl_key
- mal_key
- opensubtitles_key
- subdl_key
- nginx_key
password123
Username
hello@world.com
@@ -520,14 +391,6 @@
NewSiteName
https://example.com
Language code (en)
-
%1$s %2$s
account
Log out
@@ -552,7 +415,6 @@
All
Max
Min
- @string/none
Outline
Depressed
Shadow
@@ -653,7 +515,7 @@
View community repositories
Public list
Uppercase all subtitles
- Warning: CloudStream 3 does not take any responsibility for using third-party extensions and does not provide any support for them!
+ Warning: CloudStream does not take any responsibility for using third-party extensions and does not provide any support for them!
%s (Disabled)
Tracks
Audio tracks
@@ -707,7 +569,6 @@
TV shows, CloudStream needs permission to run in background. By pressing "OK", you\'ll be shown a request dialog.
Please press \'Allow\'.\n\nPlease note, this permission does not mean CS3 will drain your battery. It will only operate in the background when necessary, such as
when receiving notifications or downloading videos from official extensions.
- battery_optimisation
App battery usage is already set to unrestricted
Unable to open CloudStream\'s App info.
Downloading app update…
@@ -777,7 +638,6 @@
Add
Replace
Replace All
- @string/cancel
It appears that a potentially duplicate item already exists in your library: \'%s.\'
@@ -790,7 +650,6 @@
\n\nWould you like to add this item anyway, replace the existing ones, or cancel the action?
- tv_no_focus_tag
Enter PIN
Enter PIN for %s
Enter Current PIN
@@ -827,27 +686,21 @@
Code expires in %1$dm %2$ds
Release Date (New to Old)
Release Date (Old to New)
- hide_player_control_names_key
Hide names of the player\'s controls
- preview_seekbar_key
Seekbar preview
Enable preview thumbnail on seekbar
No subtitles loaded yet
- backup_path_key
Backup folder location
- backup_dir_key
Custom
Confirm before exiting
Show dialog before exiting the app
- confirm_exit_key
Show
Don\'t Show
Edge Size
Enable torrent in Settings/Providers/Preferred media
Restart app and accept Stream Torrent pop-up to proceed.
- software_decoding_key2
Software decoding
Software decoding enables the player to play video files not supported by your device, but may cause laggy or unstable playback on high resolution.
Volume has exceeded 100%
@@ -855,7 +708,6 @@
Update Plugins
Update plugins manually
- manual_update_plugins
Starting plugin update process!
Successfully updated %d plugin(s)!
No plugins were updated.
@@ -879,7 +731,6 @@
Overscan
Changes size of posters
Poster size
- speedup_key
LongPress Speed Toggle
Hold to get 2x speed
Edit Profile Image
@@ -903,5 +754,4 @@
Top left
Top center
Top right
- S1E1
From 2795e9e0e2856dfd4d43177eb0be4981da1270b3 Mon Sep 17 00:00:00 2001
From: Bnyro
Date: Sun, 4 Jan 2026 10:08:15 +0100
Subject: [PATCH 685/956] feat(extractors): add vidnest extractor (#2390)
---
.../lagradost/cloudstream3/extractors/AsianLoad.kt | 12 +++++++-----
.../com/lagradost/cloudstream3/utils/ExtractorApi.kt | 2 ++
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/AsianLoad.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/AsianLoad.kt
index 256681679..70e869f55 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/AsianLoad.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/AsianLoad.kt
@@ -4,9 +4,12 @@ import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.ExtractorApi
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.M3u8Helper
-import com.lagradost.cloudstream3.utils.getQualityFromName
import com.lagradost.cloudstream3.utils.newExtractorLink
-import java.net.URI
+
+class Vidnest : AsianLoad() {
+ override var name = "Vidnest"
+ override var mainUrl = "https://vidnest.io"
+}
open class AsianLoad : ExtractorApi() {
override var name = "AsianLoad"
@@ -20,7 +23,7 @@ open class AsianLoad : ExtractorApi() {
sourceRegex.findAll(this.text).forEach { sourceMatch ->
val extractedUrl = sourceMatch.groupValues[1]
// Trusting this isn't mp4, may fuck up stuff
- if (URI(extractedUrl).path.endsWith(".m3u8")) {
+ if (extractedUrl.contains(".m3u8")) {
M3u8Helper.generateM3u8(
name,
extractedUrl,
@@ -29,7 +32,7 @@ open class AsianLoad : ExtractorApi() {
).forEach { link ->
extractedLinksList.add(link)
}
- } else if (extractedUrl.endsWith(".mp4")) {
+ } else if (extractedUrl.contains(".mp4")) {
extractedLinksList.add(
newExtractorLink(
source = name,
@@ -37,7 +40,6 @@ open class AsianLoad : ExtractorApi() {
url = extractedUrl,
) {
this.referer = url.replace(" ", "%20")
- this.quality = getQualityFromName(sourceMatch.groupValues[2])
}
)
}
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index 641c91319..b9a147fc7 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -263,6 +263,7 @@ import com.lagradost.cloudstream3.extractors.Vidguardto3
import com.lagradost.cloudstream3.extractors.VidhideExtractor
import com.lagradost.cloudstream3.extractors.Vidmoly
import com.lagradost.cloudstream3.extractors.Vidmolyme
+import com.lagradost.cloudstream3.extractors.Vidnest
import com.lagradost.cloudstream3.extractors.Vido
import com.lagradost.cloudstream3.extractors.Vidstreamz
import com.lagradost.cloudstream3.extractors.VinovoSi
@@ -1169,6 +1170,7 @@ val extractorApis: MutableList = arrayListOf(
FlaswishCom(),
SfastwishCom(),
Playerwish(),
+ Vidnest(),
EmturbovidExtractor(),
Vtbe(),
EPlayExtractor(),
From dc6b9f435d988cd09c7d101eb1c043ccdb4a4e3c Mon Sep 17 00:00:00 2001
From: Bnyro
Date: Sun, 4 Jan 2026 10:09:50 +0100
Subject: [PATCH 686/956] feat(extractors): add up4stream extractor (#2389)
---
.../cloudstream3/extractors/Up4Stream.kt | 60 +++++++++++++++++++
.../cloudstream3/utils/ExtractorApi.kt | 4 ++
2 files changed, 64 insertions(+)
create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Up4Stream.kt
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Up4Stream.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Up4Stream.kt
new file mode 100644
index 000000000..91150992b
--- /dev/null
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/extractors/Up4Stream.kt
@@ -0,0 +1,60 @@
+package com.lagradost.cloudstream3.extractors
+
+import com.lagradost.api.Log
+import com.lagradost.cloudstream3.app
+import com.lagradost.cloudstream3.utils.ExtractorApi
+import com.lagradost.cloudstream3.utils.ExtractorLink
+import com.lagradost.cloudstream3.utils.JsUnpacker
+import com.lagradost.cloudstream3.utils.Qualities
+import com.lagradost.cloudstream3.utils.fixUrl
+import com.lagradost.cloudstream3.utils.newExtractorLink
+import kotlinx.coroutines.delay
+
+class Up4FunTop : Up4Stream() {
+ override var mainUrl: String = "https://up4fun.top"
+}
+
+open class Up4Stream : ExtractorApi() {
+ override var name = "Up4Stream"
+ override var mainUrl = "https://up4stream.com"
+ override val requiresReferer = true
+
+ override suspend fun getUrl(url: String, referer: String?): List? {
+ val movieId = url.substringAfterLast("/").substringBefore(".html")
+
+ // redirect from "wait 5 seconds" page to actual movie page
+ val redirectResponse = app.get(url, cookies = mapOf("id" to movieId))
+ val redirectForm = redirectResponse.document.selectFirst("form[method=POST]") ?: return null
+ val redirectUrl = fixUrl(redirectForm.attr("action"))
+ val redirectParams = redirectForm.select("input[type=hidden]").associate { input ->
+ input.attr("name") to input.attr("value")
+ }
+
+ // wait for 5 seconds, otherwise the below md5 hash is invalid
+ delay(5000)
+ val response = app.post(redirectUrl, data = redirectParams).document
+
+ // starting here, this works similar to many other extractors like StreamWish
+ val extractedpack =
+ response.selectFirst("script:containsData(function(p,a,c,k,e,d))")?.data()
+ if (extractedpack == null) {
+ Log.e("up4stream", "file not ready: delay too short")
+ }
+
+ JsUnpacker(extractedpack).unpack()?.let { unPacked ->
+ Regex("sources:\\[\\{file:\"(.*?)\"").find(unPacked)?.groupValues?.get(1)?.let { link ->
+ return listOf(
+ newExtractorLink(
+ this.name,
+ this.name,
+ link,
+ ) {
+ this.referer = referer.orEmpty()
+ this.quality = Qualities.Unknown.value
+ }
+ )
+ }
+ }
+ return null
+ }
+}
\ No newline at end of file
diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
index b9a147fc7..c0d5c5da7 100644
--- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
+++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/ExtractorApi.kt
@@ -230,6 +230,8 @@ import com.lagradost.cloudstream3.extractors.Techinmind
import com.lagradost.cloudstream3.extractors.Tomatomatela
import com.lagradost.cloudstream3.extractors.TomatomatelalClub
import com.lagradost.cloudstream3.extractors.Tubeless
+import com.lagradost.cloudstream3.extractors.Up4FunTop
+import com.lagradost.cloudstream3.extractors.Up4Stream
import com.lagradost.cloudstream3.extractors.Upstream
import com.lagradost.cloudstream3.extractors.UpstreamExtractor
import com.lagradost.cloudstream3.extractors.Uqload
@@ -1223,6 +1225,8 @@ val extractorApis: MutableList = arrayListOf(
VkExtractor(),
Bysezejataos(),
ByseSX(),
+ Up4Stream(),
+ Up4FunTop()
)
From 5e54552338eca91ec7b792e54dd0e5da28d17bf5 Mon Sep 17 00:00:00 2001
From: rockhero1234 <149141736+rockhero1234@users.noreply.github.com>
Date: Sun, 4 Jan 2026 15:59:04 +0530
Subject: [PATCH 687/956] remove check icon in tvtype chips (#2363)
---
app/src/main/res/values/styles.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index eb5a57c03..8cf61eaea 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -102,6 +102,7 @@
- @font/google_sans
- @string/tv_no_focus_tag
- 0dp
+ - false
+
+
+