mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'recloudstream:master' into master
This commit is contained in:
commit
46bab40d75
8 changed files with 161 additions and 74 deletions
|
@ -84,6 +84,8 @@ import com.lagradost.nicehttp.Requests
|
||||||
import com.lagradost.nicehttp.ResponseParser
|
import com.lagradost.nicehttp.ResponseParser
|
||||||
import kotlinx.android.synthetic.main.activity_main.*
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
@ -131,6 +133,10 @@ var app = Requests(responseParser = object : ResponseParser {
|
||||||
class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "MAINACT"
|
const val TAG = "MAINACT"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fires every time a new batch of plugins have been loaded, no guarantee about how often this is run and on which thread
|
||||||
|
* */
|
||||||
val afterPluginsLoadedEvent = Event<Boolean>()
|
val afterPluginsLoadedEvent = Event<Boolean>()
|
||||||
val mainPluginsLoadedEvent =
|
val mainPluginsLoadedEvent =
|
||||||
Event<Boolean>() // homepage api, used to speed up time to load for homepage
|
Event<Boolean>() // homepage api, used to speed up time to load for homepage
|
||||||
|
@ -236,6 +242,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
afterPluginsLoadedEvent += ::onAllPluginsLoaded
|
||||||
try {
|
try {
|
||||||
if (isCastApiAvailable()) {
|
if (isCastApiAvailable()) {
|
||||||
//mCastSession = mSessionManager.currentCastSession
|
//mCastSession = mSessionManager.currentCastSession
|
||||||
|
@ -325,6 +332,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
broadcastIntent.action = "restart_service"
|
broadcastIntent.action = "restart_service"
|
||||||
broadcastIntent.setClass(this, VideoDownloadRestartReceiver::class.java)
|
broadcastIntent.setClass(this, VideoDownloadRestartReceiver::class.java)
|
||||||
this.sendBroadcast(broadcastIntent)
|
this.sendBroadcast(broadcastIntent)
|
||||||
|
afterPluginsLoadedEvent -= ::onAllPluginsLoaded
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,6 +422,36 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val pluginsLock = Mutex()
|
||||||
|
private fun onAllPluginsLoaded(success: Boolean = false) {
|
||||||
|
ioSafe {
|
||||||
|
pluginsLock.withLock {
|
||||||
|
// Load cloned sites after plugins have been loaded since clones depend on plugins.
|
||||||
|
try {
|
||||||
|
getKey<Array<SettingsGeneral.CustomSite>>(USER_PROVIDER_API)?.let { list ->
|
||||||
|
list.forEach { custom ->
|
||||||
|
allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass }
|
||||||
|
?.let {
|
||||||
|
allProviders.add(it.javaClass.newInstance().apply {
|
||||||
|
name = custom.name
|
||||||
|
lang = custom.lang
|
||||||
|
mainUrl = custom.url.trimEnd('/')
|
||||||
|
canBeOverridden = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it.hashCode() is not enough to make sure they are distinct
|
||||||
|
apis = allProviders.distinctBy { it.lang + it.name + it.mainUrl + it.javaClass.name }
|
||||||
|
APIHolder.apiMap = null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
logError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
app.initClient(this)
|
app.initClient(this)
|
||||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
@ -446,36 +484,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
mainPluginsLoadedEvent.invoke(false)
|
mainPluginsLoadedEvent.invoke(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settingsManager.getBoolean(getString(R.string.auto_update_plugins_key), true)) {
|
ioSafe {
|
||||||
PluginManager.updateAllOnlinePluginsAndLoadThem(this@MainActivity)
|
if (settingsManager.getBoolean(getString(R.string.auto_update_plugins_key), true)) {
|
||||||
} else {
|
PluginManager.updateAllOnlinePluginsAndLoadThem(this@MainActivity)
|
||||||
PluginManager.loadAllOnlinePlugins(this@MainActivity)
|
} else {
|
||||||
}
|
PluginManager.loadAllOnlinePlugins(this@MainActivity)
|
||||||
|
|
||||||
PluginManager.loadAllLocalPlugins(this@MainActivity)
|
|
||||||
|
|
||||||
// Load cloned sites after plugins have been loaded since clones depend on plugins.
|
|
||||||
try {
|
|
||||||
getKey<Array<SettingsGeneral.CustomSite>>(USER_PROVIDER_API)?.let { list ->
|
|
||||||
list.forEach { custom ->
|
|
||||||
allProviders.firstOrNull { it.javaClass.simpleName == custom.parentJavaClass }
|
|
||||||
?.let {
|
|
||||||
allProviders.add(it.javaClass.newInstance().apply {
|
|
||||||
name = custom.name
|
|
||||||
lang = custom.lang
|
|
||||||
mainUrl = custom.url.trimEnd('/')
|
|
||||||
canBeOverridden = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
apis = allProviders.distinctBy { it }
|
|
||||||
APIHolder.apiMap = null
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logError(e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
afterPluginsLoadedEvent.invoke(true)
|
ioSafe {
|
||||||
|
PluginManager.loadAllLocalPlugins(this@MainActivity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ioSafe {
|
// ioSafe {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import com.lagradost.nicehttp.Requests.Companion.await
|
||||||
import com.lagradost.nicehttp.cookies
|
import com.lagradost.nicehttp.cookies
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
|
import java.net.URI
|
||||||
|
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
|
@ -30,6 +31,17 @@ class CloudflareKiller : Interceptor {
|
||||||
|
|
||||||
val savedCookies: MutableMap<String, Map<String, String>> = mutableMapOf()
|
val savedCookies: MutableMap<String, Map<String, String>> = mutableMapOf()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the headers with cookies, webview user agent included!
|
||||||
|
* */
|
||||||
|
fun getCookieHeaders(url: String): Headers {
|
||||||
|
val userAgentHeaders = WebViewResolver.webViewUserAgent?.let {
|
||||||
|
mapOf("user-agent" to it)
|
||||||
|
} ?: emptyMap()
|
||||||
|
|
||||||
|
return getHeaders(userAgentHeaders, savedCookies[URI(url).host] ?: emptyMap())
|
||||||
|
}
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response = runBlocking {
|
override fun intercept(chain: Interceptor.Chain): Response = runBlocking {
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val cookies = savedCookies[request.url.host]
|
val cookies = savedCookies[request.url.host]
|
||||||
|
|
|
@ -21,12 +21,15 @@ import com.lagradost.cloudstream3.ui.settings.extensions.REPOSITORIES_KEY
|
||||||
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData
|
||||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
import com.lagradost.cloudstream3.utils.VideoDownloadManager.sanitizeFilename
|
||||||
import com.lagradost.cloudstream3.APIHolder.removePluginMapping
|
import com.lagradost.cloudstream3.APIHolder.removePluginMapping
|
||||||
|
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES
|
||||||
|
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||||
import com.lagradost.cloudstream3.utils.extractorApis
|
import com.lagradost.cloudstream3.utils.extractorApis
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import org.acra.log.debug
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -162,11 +165,16 @@ object PluginManager {
|
||||||
val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN
|
val isDisabled = onlineData.second.status == PROVIDER_STATUS_DOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
|
// var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
|
||||||
|
|
||||||
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean {
|
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean {
|
||||||
return (getPluginsOnline().firstOrNull { it.internalName == apiName }
|
return (getPluginsOnline().firstOrNull {
|
||||||
?: getPluginsLocal().firstOrNull { it.internalName == apiName })?.let { savedData ->
|
// Most of the time the provider ends with Provider which isn't part of the api name
|
||||||
|
it.internalName.replace("provider", "", ignoreCase = true) == apiName
|
||||||
|
}
|
||||||
|
?: getPluginsLocal().firstOrNull {
|
||||||
|
it.internalName.replace("provider", "", ignoreCase = true) == apiName
|
||||||
|
})?.let { savedData ->
|
||||||
// OnlinePluginData(savedData, onlineData)
|
// OnlinePluginData(savedData, onlineData)
|
||||||
loadPlugin(
|
loadPlugin(
|
||||||
activity,
|
activity,
|
||||||
|
@ -184,6 +192,19 @@ object PluginManager {
|
||||||
* 4. Else load the plugin normally
|
* 4. Else load the plugin normally
|
||||||
**/
|
**/
|
||||||
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
|
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
|
||||||
|
// Load all plugins as fast as possible!
|
||||||
|
(getPluginsOnline()).toList().apmap { pluginData ->
|
||||||
|
loadPlugin(
|
||||||
|
activity,
|
||||||
|
File(pluginData.filePath),
|
||||||
|
pluginData
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ioSafe {
|
||||||
|
afterPluginsLoadedEvent.invoke(true)
|
||||||
|
}
|
||||||
|
|
||||||
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
|
||||||
?: emptyArray()) + PREBUILT_REPOSITORIES
|
?: emptyArray()) + PREBUILT_REPOSITORIES
|
||||||
|
|
||||||
|
@ -198,29 +219,28 @@ object PluginManager {
|
||||||
OnlinePluginData(savedData, onlineData)
|
OnlinePluginData(savedData, onlineData)
|
||||||
}
|
}
|
||||||
}.flatten().distinctBy { it.onlineData.second.url }
|
}.flatten().distinctBy { it.onlineData.second.url }
|
||||||
allCurrentOutDatedPlugins = outdatedPlugins.toSet()
|
|
||||||
|
|
||||||
Log.i(TAG, "Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}")
|
debug {
|
||||||
|
"Outdated plugins: ${outdatedPlugins.filter { it.isOutdated }}"
|
||||||
|
}
|
||||||
|
|
||||||
outdatedPlugins.apmap {
|
outdatedPlugins.apmap { pluginData ->
|
||||||
if (it.isDisabled) {
|
if (pluginData.isDisabled) {
|
||||||
return@apmap
|
unloadPlugin(pluginData.savedData.filePath)
|
||||||
} else if (it.isOutdated) {
|
} else if (pluginData.isOutdated) {
|
||||||
downloadAndLoadPlugin(
|
downloadAndLoadPlugin(
|
||||||
activity,
|
activity,
|
||||||
it.onlineData.second.url,
|
pluginData.onlineData.second.url,
|
||||||
it.savedData.internalName,
|
pluginData.savedData.internalName,
|
||||||
it.onlineData.first
|
pluginData.onlineData.first
|
||||||
)
|
|
||||||
} else {
|
|
||||||
loadPlugin(
|
|
||||||
activity,
|
|
||||||
File(it.savedData.filePath),
|
|
||||||
it.savedData
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ioSafe {
|
||||||
|
afterPluginsLoadedEvent.invoke(true)
|
||||||
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Plugin update done!")
|
Log.i(TAG, "Plugin update done!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +276,7 @@ object PluginManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadedLocalPlugins = true
|
loadedLocalPlugins = true
|
||||||
|
afterPluginsLoadedEvent.invoke(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,7 +401,9 @@ object PluginManager {
|
||||||
try {
|
try {
|
||||||
val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
|
val folderName = getPluginSanitizedFileName(repositoryUrl) // Guaranteed unique
|
||||||
val fileName = getPluginSanitizedFileName(internalName)
|
val fileName = getPluginSanitizedFileName(internalName)
|
||||||
Log.i(TAG, "Downloading plugin: $pluginUrl to $folderName/$fileName")
|
unloadPlugin("${activity.filesDir}/${ONLINE_PLUGINS_FOLDER}/${folderName}/$fileName.cs3")
|
||||||
|
|
||||||
|
Log.d(TAG, "Downloading plugin: $pluginUrl to $folderName/$fileName")
|
||||||
// The plugin file needs to be salted with the repository url hash as to allow multiple repositories with the same internal plugin names
|
// The plugin file needs to be salted with the repository url hash as to allow multiple repositories with the same internal plugin names
|
||||||
val file = downloadPluginToFile(activity, pluginUrl, fileName, folderName)
|
val file = downloadPluginToFile(activity, pluginUrl, fileName, folderName)
|
||||||
return loadPlugin(
|
return loadPlugin(
|
||||||
|
|
|
@ -456,20 +456,17 @@ class HomeFragment : Fragment() {
|
||||||
homeViewModel.loadStoredData(list)
|
homeViewModel.loadStoredData(list)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var hasBeenConsumed = false
|
|
||||||
private fun firstLoadHomePage(successful: Boolean = false) {
|
private fun firstLoadHomePage(successful: Boolean = false) {
|
||||||
// dirty hack to make it only load once
|
// dirty hack to make it only load once
|
||||||
if(hasBeenConsumed) return
|
loadHomePage(false)
|
||||||
hasBeenConsumed = true
|
|
||||||
loadHomePage(successful)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadHomePage(successful: Boolean = false) {
|
private fun loadHomePage(forceReload: Boolean = true) {
|
||||||
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
|
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
|
||||||
|
|
||||||
if (homeViewModel.apiName.value != apiName || apiName == null) {
|
if (homeViewModel.apiName.value != apiName || apiName == null) {
|
||||||
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
|
//println("Caught home: " + homeViewModel.apiName.value + " at " + apiName)
|
||||||
homeViewModel.loadAndCancel(apiName)
|
homeViewModel.loadAndCancel(apiName, forceReload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -264,25 +264,33 @@ class HomeViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadAndCancel(preferredApiName: String?) = viewModelScope.launchSafe {
|
fun loadAndCancel(preferredApiName: String?, forceReload: Boolean = true) =
|
||||||
val api = getApiFromNameNull(preferredApiName)
|
viewModelScope.launchSafe {
|
||||||
if (preferredApiName == noneApi.name){
|
// Since plugins are loaded in stages this function can get called multiple times.
|
||||||
setKey(USER_SELECTED_HOMEPAGE_API, noneApi.name)
|
// The issue with this is that the homepage may be fetched multiple times while the first request is loading
|
||||||
loadAndCancel(noneApi)
|
val api = getApiFromNameNull(preferredApiName)
|
||||||
}
|
if (!forceReload && api?.let { expandable[it.name]?.list?.list?.isNotEmpty() } == true) {
|
||||||
else if (preferredApiName == randomApi.name || api == null) {
|
return@launchSafe
|
||||||
val validAPIs = context?.filterProviderByPreferredMedia()
|
}
|
||||||
if (validAPIs.isNullOrEmpty()) {
|
// If the plugin isn't loaded yet. (Does not set the key)
|
||||||
// Do not set USER_SELECTED_HOMEPAGE_API when there is no plugins loaded
|
if (api == null) {
|
||||||
loadAndCancel(noneApi)
|
loadAndCancel(noneApi)
|
||||||
} else {
|
} else if (preferredApiName == noneApi.name) {
|
||||||
val apiRandom = validAPIs.random()
|
setKey(USER_SELECTED_HOMEPAGE_API, noneApi.name)
|
||||||
loadAndCancel(apiRandom)
|
loadAndCancel(noneApi)
|
||||||
setKey(USER_SELECTED_HOMEPAGE_API, apiRandom.name)
|
} else if (preferredApiName == randomApi.name) {
|
||||||
|
val validAPIs = context?.filterProviderByPreferredMedia()
|
||||||
|
if (validAPIs.isNullOrEmpty()) {
|
||||||
|
// Do not set USER_SELECTED_HOMEPAGE_API when there is no plugins loaded
|
||||||
|
loadAndCancel(noneApi)
|
||||||
|
} else {
|
||||||
|
val apiRandom = validAPIs.random()
|
||||||
|
loadAndCancel(apiRandom)
|
||||||
|
setKey(USER_SELECTED_HOMEPAGE_API, apiRandom.name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setKey(USER_SELECTED_HOMEPAGE_API, api.name)
|
||||||
|
loadAndCancel(api)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
setKey(USER_SELECTED_HOMEPAGE_API, api.name)
|
|
||||||
loadAndCancel(api)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -6,6 +6,7 @@ import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.exoplayer2.*
|
import com.google.android.exoplayer2.*
|
||||||
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
import com.google.android.exoplayer2.database.StandaloneDatabaseProvider
|
||||||
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource
|
||||||
|
@ -742,7 +743,15 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
}
|
}
|
||||||
Player.STATE_ENDED -> {
|
Player.STATE_ENDED -> {
|
||||||
handleEvent(CSPlayerEvent.NextEpisode)
|
// Only play next episode if autoplay is on (default)
|
||||||
|
if (PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
?.getBoolean(
|
||||||
|
context.getString(com.lagradost.cloudstream3.R.string.autoplay_next_key),
|
||||||
|
true
|
||||||
|
) == true
|
||||||
|
) {
|
||||||
|
handleEvent(CSPlayerEvent.NextEpisode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Player.STATE_BUFFERING -> {
|
Player.STATE_BUFFERING -> {
|
||||||
updatedTime()
|
updatedTime()
|
||||||
|
@ -780,7 +789,15 @@ class CS3IPlayer : IPlayer {
|
||||||
|
|
||||||
}
|
}
|
||||||
Player.STATE_ENDED -> {
|
Player.STATE_ENDED -> {
|
||||||
handleEvent(CSPlayerEvent.NextEpisode)
|
// Only play next episode if autoplay is on (default)
|
||||||
|
if (PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
?.getBoolean(
|
||||||
|
context.getString(com.lagradost.cloudstream3.R.string.autoplay_next_key),
|
||||||
|
true
|
||||||
|
) == true
|
||||||
|
) {
|
||||||
|
handleEvent(CSPlayerEvent.NextEpisode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Player.STATE_BUFFERING -> {
|
Player.STATE_BUFFERING -> {
|
||||||
updatedTime()
|
updatedTime()
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
<string name="double_tap_pause_enabled_key" translatable="false">double_tap_pause_enabled_key</string>
|
<string name="double_tap_pause_enabled_key" translatable="false">double_tap_pause_enabled_key</string>
|
||||||
<string name="double_tap_seek_time_key" translatable="false">double_tap_seek_time_key</string>
|
<string name="double_tap_seek_time_key" translatable="false">double_tap_seek_time_key</string>
|
||||||
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
|
<string name="swipe_vertical_enabled_key" translatable="false">swipe_vertical_enabled_key</string>
|
||||||
|
<string name="autoplay_next_key" translatable="false">autoplay_next_key</string>
|
||||||
<string name="display_sub_key" translatable="false">display_sub_key</string>
|
<string name="display_sub_key" translatable="false">display_sub_key</string>
|
||||||
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
|
<string name="show_fillers_key" translatable="false">show_fillers_key</string>
|
||||||
<string name="show_trailers_key" translatable="false">show_trailers_key</string>
|
<string name="show_trailers_key" translatable="false">show_trailers_key</string>
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
<string name="primary_color_key" translatable="false">primary_color_key</string>
|
<string name="primary_color_key" translatable="false">primary_color_key</string>
|
||||||
<string name="restore_key" translatable="false">restore_key</string>
|
<string name="restore_key" translatable="false">restore_key</string>
|
||||||
<string name="backup_key" translatable="false">backup_key</string>
|
<string name="backup_key" translatable="false">backup_key</string>
|
||||||
<string name="prefer_media_type_key" translatable="false">prefer_media_type_key</string>
|
<string name="prefer_media_type_key" translatable="false">prefer_media_type_key_2</string>
|
||||||
<string name="app_theme_key" translatable="false">app_theme_key</string>
|
<string name="app_theme_key" translatable="false">app_theme_key</string>
|
||||||
<string name="episode_sync_enabled_key" translatable="false">episode_sync_enabled_key</string>
|
<string name="episode_sync_enabled_key" translatable="false">episode_sync_enabled_key</string>
|
||||||
<string name="log_enabled_key" translatable="false">log_enabled_key</string>
|
<string name="log_enabled_key" translatable="false">log_enabled_key</string>
|
||||||
|
@ -223,6 +224,10 @@
|
||||||
<string name="swipe_to_seek_settings_des">Swipe left or right to control time in the videoplayer</string>
|
<string name="swipe_to_seek_settings_des">Swipe left or right to control time in the videoplayer</string>
|
||||||
<string name="swipe_to_change_settings">Swipe to change settings</string>
|
<string name="swipe_to_change_settings">Swipe to change settings</string>
|
||||||
<string name="swipe_to_change_settings_des">Swipe on the left or right side to change brightness or volume</string>
|
<string name="swipe_to_change_settings_des">Swipe on the left or right side to change brightness or volume</string>
|
||||||
|
|
||||||
|
<string name="autoplay_next_settings">Autoplay next episode</string>
|
||||||
|
<string name="autoplay_next_settings_des">Start the next episode when the current one ends</string>
|
||||||
|
|
||||||
<string name="double_tap_to_seek_settings">Double tap to seek</string>
|
<string name="double_tap_to_seek_settings">Double tap to seek</string>
|
||||||
<string name="double_tap_to_pause_settings">Double tap to pause</string>
|
<string name="double_tap_to_pause_settings">Double tap to pause</string>
|
||||||
<string name="double_tap_to_seek_amount_settings">Player seek amount</string>
|
<string name="double_tap_to_seek_amount_settings">Player seek amount</string>
|
||||||
|
|
|
@ -58,6 +58,12 @@
|
||||||
android:title="@string/swipe_to_change_settings"
|
android:title="@string/swipe_to_change_settings"
|
||||||
android:summary="@string/swipe_to_change_settings_des"
|
android:summary="@string/swipe_to_change_settings_des"
|
||||||
app:defaultValue="true" />
|
app:defaultValue="true" />
|
||||||
|
<SwitchPreference
|
||||||
|
android:icon="@drawable/ic_baseline_skip_next_24"
|
||||||
|
app:key="@string/autoplay_next_key"
|
||||||
|
android:title="@string/autoplay_next_settings"
|
||||||
|
android:summary="@string/autoplay_next_settings_des"
|
||||||
|
app:defaultValue="true" />
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:icon="@drawable/ic_baseline_touch_app_24"
|
android:icon="@drawable/ic_baseline_touch_app_24"
|
||||||
app:key="@string/double_tap_enabled_key"
|
app:key="@string/double_tap_enabled_key"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue