From 6baeadf55e108aeed1a3557ae5213e450f5b5ede Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Fri, 16 Sep 2022 17:01:16 +0200 Subject: [PATCH 001/273] let extensions patch all resources --- .../lagradost/cloudstream3/AcraApplication.kt | 2 + .../lagradost/cloudstream3/MainActivity.kt | 22 ++++- .../lagradost/cloudstream3/plugins/Plugin.kt | 12 +++ .../cloudstream3/plugins/PluginManager.kt | 2 + .../ui/player/DownloadedPlayerActivity.kt | 16 +++- .../cloudstream3/ui/settings/SettingsUI.kt | 24 ++++++ .../utils/resources/MapResourcePatch.kt | 47 +++++++++++ .../utils/resources/ResourcePackManager.kt | 66 +++++++++++++++ .../utils/resources/ResourcePatch.kt | 80 +++++++++++++++++++ .../utils/resources/ResourcePatchActivity.kt | 5 ++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/settins_ui.xml | 4 + 12 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatch.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatchActivity.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index 85f031b3..f343bb0c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -18,6 +18,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.resources.ResourcePackManager import kotlinx.coroutines.runBlocking import org.acra.ACRA import org.acra.ReportField @@ -79,6 +80,7 @@ class ExceptionHandler(val errorFile: File, val onError: (() -> Unit)): Thread.U ACRA.errorReporter.handleException(error) try { PrintStream(errorFile).use { ps -> + ps.println(String.format("Enabled resource pack: ${ResourcePackManager.activePackId ?: "none"}")) ps.println(String.format("Currently loading extension: ${PluginManager.currentlyLoading ?: "none"}")) ps.println(String.format("Fatal exception on thread %s (%d)", thread.name, thread.id)) error.printStackTrace(ps) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 6c9fadd8..9bc36847 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -4,13 +4,13 @@ import android.content.ComponentName import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.content.res.Resources import android.os.Bundle import android.util.Log import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.WindowManager -import android.widget.Toast import androidx.annotation.IdRes import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -59,6 +59,9 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSet import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions +import com.lagradost.cloudstream3.utils.Event +import com.lagradost.cloudstream3.utils.IOnBackPressed +import com.lagradost.cloudstream3.utils.resources.ResourcePatch import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadRepository @@ -70,8 +73,6 @@ import com.lagradost.cloudstream3.utils.DataStore.removeKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos -import com.lagradost.cloudstream3.utils.Event -import com.lagradost.cloudstream3.utils.IOnBackPressed import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.checkWrite @@ -82,6 +83,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API +import com.lagradost.cloudstream3.utils.resources.ResourcePackManager +import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* @@ -133,7 +136,18 @@ var app = Requests(responseParser = object : ResponseParser { defaultHeaders = mapOf("user-agent" to USER_AGENT) } -class MainActivity : AppCompatActivity(), ColorPickerDialogListener { +class MainActivity : AppCompatActivity(), ColorPickerDialogListener, ResourcePatchActivity { + private var resourcePatch: ResourcePatch? = null + + override fun getResources(): Resources = resourcePatch ?: super.getResources() + override fun reloadResourcePatch() { + resourcePatch = try { + ResourcePackManager.activePack?.invoke(super.getResources()) + } catch (e: Throwable) { + null + } + } + companion object { const val TAG = "MAINACT" 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 242baf59..ec845d2c 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.utils.resources.ResourcePackManager +import com.lagradost.cloudstream3.utils.resources.ResourcePatch const val PLUGIN_TAG = "PluginInstance" @@ -50,6 +52,16 @@ abstract class Plugin { extractorApis.add(element) } + /** + * Used to register a new resource pack + * @param factory The function that provided the original resources will generate a ResourcePatch instance + * You should probably use MapResourcePatch + * @see com.lagradost.cloudstream3.utils.resources.MapResourcePatch + */ + fun registerResourcePack(name: String, factory: (Resources) -> ResourcePatch) { + ResourcePackManager.registerPack(name, factory, this.__filename) + } + class Manifest { @JsonProperty("name") var name: String? = null @JsonProperty("pluginClassName") var pluginClassName: 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 b5441b94..29603a5f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIE import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.extractorApis +import com.lagradost.cloudstream3.utils.resources.ResourcePackManager import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.acra.log.debug @@ -376,6 +377,7 @@ object PluginManager { } APIHolder.allProviders.removeIf { provider: MainAPI -> provider.sourcePlugin == plugin.__filename } extractorApis.removeIf { provider: ExtractorApi -> provider.sourcePlugin == plugin.__filename } + ResourcePackManager.unregisterPlugin(plugin.__filename) classLoaders.values.removeIf { v -> v == plugin } 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 dc1bbba3..0d9c819c 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 @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.ui.player import android.content.Intent +import android.content.res.Resources import android.net.Uri import android.os.Bundle import android.util.Log @@ -11,10 +12,13 @@ import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.cloudstream3.utils.resources.ResourcePackManager +import com.lagradost.cloudstream3.utils.resources.ResourcePatch +import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity const val DTAG = "PlayerActivity" -class DownloadedPlayerActivity : AppCompatActivity() { +class DownloadedPlayerActivity : AppCompatActivity(), ResourcePatchActivity { override fun dispatchKeyEvent(event: KeyEvent?): Boolean { CommonActivity.dispatchKeyEvent(this, event)?.let { return it @@ -106,4 +110,14 @@ class DownloadedPlayerActivity : AppCompatActivity() { return } } + + private var resourcePatch: ResourcePatch? = null + override fun getResources(): Resources = resourcePatch ?: super.getResources() + override fun reloadResourcePatch() { + resourcePatch = try { + ResourcePackManager.activePack?.invoke(super.getResources()) + } catch (e: Throwable) { + null + } + } } \ No newline at end of file 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 9ad3ee8f..b01d900e 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 @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.View import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchQuality import com.lagradost.cloudstream3.mvvm.logError @@ -16,6 +17,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.resources.ResourcePackManager +import kotlin.streams.toList class SettingsUI : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -130,6 +133,27 @@ class SettingsUI : PreferenceFragmentCompat() { } return@setOnPreferenceClickListener true } + getPref(R.string.active_resource_pack_key)?.setOnPreferenceClickListener { + val prefNames = ResourcePackManager.packs.keys.toMutableList() + prefNames.add(0, getString(R.string.none)) + val prefValues: MutableList = ResourcePackManager.packs.keys.toMutableList() + prefValues.add(0, null) + + activity?.showDialog( + prefNames.toList(), + prefValues.indexOf(ResourcePackManager.activePackId), + getString(R.string.resource_pack), + true, + {}) { + try { + ResourcePackManager.selectPack(prefValues[it], activity as MainActivity) + activity?.recreate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } getPref(R.string.pref_filter_search_quality_key)?.setOnPreferenceClickListener { val names = enumValues().sorted().map { it.name } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt new file mode 100644 index 00000000..732a95cc --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt @@ -0,0 +1,47 @@ +package com.lagradost.cloudstream3.utils.resources + +import android.content.res.ColorStateList +import android.content.res.Resources +import android.content.res.XmlResourceParser +import android.graphics.drawable.Drawable +import android.os.Build +import android.util.Log +import androidx.annotation.RequiresApi + +typealias Generator = () -> T? +typealias GeneratorWithTheme = (Resources.Theme?) -> T? + +open class MapResourcePatch(private val original: Resources): ResourcePatch(original) { + val mapping: HashMap = hashMapOf() + val idMapping: HashMap = hashMapOf() + + override fun mapId(id: Int): Int = + idMapping[id] ?: super.mapId(id) + + override fun getLayout(id: Int): XmlResourceParser = + mapping.getMappingKA(id, null) ?: super.getLayout(id) + + override fun getDrawable(id: Int, theme: Theme?): Drawable = + mapping.getMappingKA(id, theme) ?: super.getDrawable(id, theme) + + @RequiresApi(Build.VERSION_CODES.M) + override fun getColorStateList(id: Int, theme: Theme?): ColorStateList = + mapping.getMappingKA(id, theme) ?: super.getColorStateList(id, theme) + + override fun getXml(id: Int): XmlResourceParser = + mapping.getMappingKA(id, null) ?: super.getXml(id) + + @RequiresApi(Build.VERSION_CODES.M) + override fun getColor(id: Int, theme: Theme?): Int = + mapping.getMappingKA(id, theme) ?: super.getColor(id, theme) +} + +private inline fun HashMap.getMappingKA(id: K?, theme: Resources.Theme?): V? { + return when (val res = this[id]) { + equals(null) -> null + is V -> res + is Function0<*> -> (res as? Generator)?.invoke() + is Function1<*, *> -> (res as? GeneratorWithTheme)?.invoke(theme) + else -> null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt new file mode 100644 index 00000000..f9d8c31c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt @@ -0,0 +1,66 @@ +package com.lagradost.cloudstream3.utils.resources + +import android.content.res.Resources +import android.util.Log +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent +import com.lagradost.cloudstream3.R + +typealias PatchFactory = (Resources) -> ResourcePatch + +object ResourcePackManager { + private const val KEY = "ResourcePackManager" + const val SETTINGS_KEY = "active_resource_pack_key" // TODO + val packs: HashMap = hashMapOf() + private val pluginMapping: HashMap> = hashMapOf() + var activePackId: String? = null + internal set + val activePack: PatchFactory? + get() = if (activePackId == null) null else packs[activePackId] + + init { + afterPluginsLoadedEvent += ::onExtensionsLoaded + } + + fun registerPack(name: String, factory: PatchFactory, pluginId: String?) { + packs[name] = factory + if (pluginId != null) { + if (!pluginMapping.containsKey(pluginId)) + pluginMapping[pluginId] = arrayListOf() + pluginMapping[pluginId]?.add(name) + } + } + + fun unregisterPlugin(pluginId: String?) { + pluginMapping[pluginId]?.forEach { + packs.remove(it) + if (activePackId == it) // if pack is being removed make sure its not set + selectPack(null, AcraApplication.context?.getActivity() as? ResourcePatchActivity) + } + pluginMapping.remove(pluginId) + } + + fun selectPack(packId: String?, activity: ResourcePatchActivity?) { + if (packId == null) activePackId = null + else if (packs.containsKey(packId)) activePackId = packId + else activePackId = null + + Log.d(KEY, "Selecting: ${activePackId ?: "none"}") + setKey(SETTINGS_KEY, activePackId) + + activity?.reloadResourcePatch() + } + + + private fun onExtensionsLoaded(successful: Boolean = false) { + if (!successful) return + activePackId = getKey(SETTINGS_KEY) + Log.d(KEY, "Selecting saved: ${activePackId ?: "none"}") + val activity = AcraApplication.context?.getActivity() as? ResourcePatchActivity + activity?.reloadResourcePatch() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatch.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatch.kt new file mode 100644 index 00000000..71bcbb2c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatch.kt @@ -0,0 +1,80 @@ +package com.lagradost.cloudstream3.utils.resources + +import android.annotation.SuppressLint +import android.content.res.* +import android.content.res.loader.ResourcesLoader +import android.graphics.Typeface +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Bundle +import android.util.AttributeSet +import android.util.DisplayMetrics +import android.util.TypedValue +import androidx.annotation.RequiresApi +import java.io.InputStream + +open class ResourcePatch(private val original: Resources) : + Resources(original.assets, original.displayMetrics, original.configuration) { + + open fun mapId(id: Int): Int = id + + @RequiresApi(Build.VERSION_CODES.R) + override fun addLoaders(vararg loaders: ResourcesLoader?) = original.addLoaders(*loaders) + override fun getAnimation(id: Int): XmlResourceParser = original.getAnimation(mapId(id)) + override fun getBoolean(id: Int): Boolean = original.getBoolean(mapId(id)) + @RequiresApi(Build.VERSION_CODES.M) + override fun getColor(id: Int, theme: Theme?): Int = original.getColor(mapId(id), theme) + override fun getConfiguration(): Configuration = original.configuration + override fun getDisplayMetrics(): DisplayMetrics = original.displayMetrics + @RequiresApi(Build.VERSION_CODES.M) + override fun getColorStateList(id: Int, theme: Theme?): ColorStateList = original.getColorStateList(mapId(id), theme) + override fun getLayout(id: Int): XmlResourceParser = original.getLayout(mapId(id)) + override fun getDimension(id: Int): Float = original.getDimension(mapId(id)) + override fun getIdentifier(name: String?, defType: String?, defPackage: String?): Int = original.getIdentifier(name, defType, defPackage) + override fun getDimensionPixelOffset(id: Int): Int = original.getDimensionPixelOffset(mapId(id)) + override fun getDimensionPixelSize(id: Int): Int = original.getDimensionPixelSize(mapId(id)) + @SuppressLint("UseCompatLoadingForDrawables") + override fun getDrawable(id: Int, theme: Theme?): Drawable = original.getDrawable(mapId(id), theme) + override fun getDrawableForDensity(id: Int, density: Int, theme: Theme?): Drawable? = original.getDrawableForDensity(mapId(id), density, theme) + @RequiresApi(Build.VERSION_CODES.Q) + override fun getFloat(id: Int): Float = original.getFloat(mapId(id)) + @RequiresApi(Build.VERSION_CODES.O) + override fun getFont(id: Int): Typeface = original.getFont(mapId(id)) + override fun getIntArray(id: Int): IntArray = original.getIntArray(mapId(id)) + override fun getFraction(id: Int, base: Int, pbase: Int): Float = original.getFraction(mapId(id), base, pbase) + override fun getInteger(id: Int): Int = original.getInteger(mapId(id)) + override fun getQuantityString(id: Int, quantity: Int): String = original.getQuantityString(mapId(id), quantity) + override fun getQuantityString(id: Int, quantity: Int, vararg formatArgs: Any?): String = original.getQuantityString(mapId(id), quantity, *formatArgs) + override fun getQuantityText(id: Int, quantity: Int): CharSequence = original.getQuantityText(mapId(id), quantity) + override fun getResourceEntryName(id: Int): String = original.getResourceEntryName(mapId(id)) + override fun getResourceName(id: Int): String = original.getResourceName(mapId(id)) + override fun getResourcePackageName(id: Int): String = original.getResourcePackageName(mapId(id)) + override fun getResourceTypeName(id: Int): String = original.getResourceTypeName(mapId(id)) + override fun getString(id: Int): String = original.getString(mapId(id)) + override fun getString(id: Int, vararg formatArgs: Any?): String = original.getString(mapId(id), *formatArgs) + override fun getStringArray(id: Int): Array = original.getStringArray(mapId(id)) + override fun getText(id: Int): CharSequence = original.getText(mapId(id)) + override fun getText(id: Int, def: CharSequence?): CharSequence = original.getText(mapId(id), def) + override fun getTextArray(id: Int): Array = original.getTextArray(mapId(id)) + override fun getValue(id: Int, outValue: TypedValue?, resolveRefs: Boolean) = original.getValue(mapId(id), outValue, resolveRefs) + override fun getValue(name: String?, outValue: TypedValue?, resolveRefs: Boolean) = original.getValue(name, outValue, resolveRefs) + override fun getValueForDensity(id: Int, density: Int, outValue: TypedValue?, resolveRefs: Boolean) = original.getValueForDensity(mapId(id), density, outValue, resolveRefs) + override fun getXml(id: Int): XmlResourceParser = original.getXml(mapId(id)) + override fun obtainTypedArray(id: Int): TypedArray = original.obtainTypedArray(mapId(id)) + override fun openRawResource(id: Int): InputStream = original.openRawResource(mapId(id)) + override fun openRawResource(id: Int, value: TypedValue?): InputStream = original.openRawResource(mapId(id), value) + override fun openRawResourceFd(id: Int): AssetFileDescriptor = original.openRawResourceFd(mapId(id)) + override fun obtainAttributes(set: AttributeSet?, attrs: IntArray?): TypedArray = original.obtainAttributes(set, attrs) + override fun parseBundleExtra(tagName: String?, attrs: AttributeSet?, outBundle: Bundle?) = original.parseBundleExtra(tagName, attrs, outBundle) + override fun parseBundleExtras(parser: XmlResourceParser?, outBundle: Bundle?) = original.parseBundleExtras(parser, outBundle) + @RequiresApi(Build.VERSION_CODES.R) + override fun removeLoaders(vararg loaders: ResourcesLoader?) = original.removeLoaders(*loaders) + override fun equals(other: Any?): Boolean = original == other + override fun hashCode(): Int = original.hashCode() + + override fun getColor(id: Int): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColor(mapId(id), null) else original.getColor(mapId(id)) + @SuppressLint("UseCompatLoadingForColorStateLists") + override fun getColorStateList(id: Int): ColorStateList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getColorStateList(mapId(id), null) else original.getColorStateList(mapId(id)) + @SuppressLint("UseCompatLoadingForDrawables") + override fun getDrawable(id: Int): Drawable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) getDrawable(mapId(id), null) else original.getDrawable(mapId(id)) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatchActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatchActivity.kt new file mode 100644 index 00000000..dc1200d5 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePatchActivity.kt @@ -0,0 +1,5 @@ +package com.lagradost.cloudstream3.utils.resources + +interface ResourcePatchActivity { + fun reloadResourcePatch() +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e6dadcf..c0008cd1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -41,6 +41,7 @@ Cloudstream app_layout_key primary_color_key + active_resource_pack_key restore_key backup_key prefer_media_type_key_2 @@ -457,6 +458,7 @@ Primary color App theme + Resource pack Poster title location Put the title under the poster diff --git a/app/src/main/res/xml/settins_ui.xml b/app/src/main/res/xml/settins_ui.xml index 071df918..bf92a17d 100644 --- a/app/src/main/res/xml/settins_ui.xml +++ b/app/src/main/res/xml/settins_ui.xml @@ -9,6 +9,10 @@ android:icon="@drawable/ic_baseline_color_lens_24" android:key="@string/app_theme_key" android:title="@string/app_theme_settings" /> + Date: Fri, 16 Sep 2022 20:16:32 +0200 Subject: [PATCH 002/273] some fixes --- .../com/lagradost/cloudstream3/MainActivity.kt | 17 ++++++++--------- .../ui/player/DownloadedPlayerActivity.kt | 14 ++++++-------- .../cloudstream3/ui/settings/SettingsUI.kt | 2 +- .../utils/resources/MapResourcePatch.kt | 2 +- .../utils/resources/ResourcePackManager.kt | 15 +++++---------- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 9bc36847..0262cd5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -84,7 +84,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.resources.ResourcePackManager -import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* @@ -136,16 +135,11 @@ var app = Requests(responseParser = object : ResponseParser { defaultHeaders = mapOf("user-agent" to USER_AGENT) } -class MainActivity : AppCompatActivity(), ColorPickerDialogListener, ResourcePatchActivity { +class MainActivity : AppCompatActivity(), ColorPickerDialogListener { private var resourcePatch: ResourcePatch? = null - override fun getResources(): Resources = resourcePatch ?: super.getResources() - override fun reloadResourcePatch() { - resourcePatch = try { - ResourcePackManager.activePack?.invoke(super.getResources()) - } catch (e: Throwable) { - null - } + override fun getResources(): Resources { + return resourcePatch ?: super.getResources() } companion object { @@ -472,6 +466,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, ResourcePat override fun onCreate(savedInstanceState: Bundle?) { app.initClient(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + resourcePatch = try { + ResourcePackManager.activePack?.invoke(super.getResources()) + } catch (e: Throwable) { + null + } val errorFile = filesDir.resolve("last_error") var lastError: String? = null 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 0d9c819c..5b55a3b3 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 @@ -18,7 +18,7 @@ import com.lagradost.cloudstream3.utils.resources.ResourcePatchActivity const val DTAG = "PlayerActivity" -class DownloadedPlayerActivity : AppCompatActivity(), ResourcePatchActivity { +class DownloadedPlayerActivity : AppCompatActivity() { override fun dispatchKeyEvent(event: KeyEvent?): Boolean { CommonActivity.dispatchKeyEvent(this, event)?.let { return it @@ -71,6 +71,11 @@ class DownloadedPlayerActivity : AppCompatActivity(), ResourcePatchActivity { override fun onCreate(savedInstanceState: Bundle?) { Log.i(DTAG, "onCreate") + resourcePatch = try { + ResourcePackManager.activePack?.invoke(super.getResources()) + } catch (e: Throwable) { + null + } CommonActivity.loadThemes(this) super.onCreate(savedInstanceState) @@ -113,11 +118,4 @@ class DownloadedPlayerActivity : AppCompatActivity(), ResourcePatchActivity { private var resourcePatch: ResourcePatch? = null override fun getResources(): Resources = resourcePatch ?: super.getResources() - override fun reloadResourcePatch() { - resourcePatch = try { - ResourcePackManager.activePack?.invoke(super.getResources()) - } catch (e: Throwable) { - null - } - } } \ No newline at end of file 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 b01d900e..61a5b130 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 @@ -146,7 +146,7 @@ class SettingsUI : PreferenceFragmentCompat() { true, {}) { try { - ResourcePackManager.selectPack(prefValues[it], activity as MainActivity) + ResourcePackManager.selectPack(prefValues[it]) activity?.recreate() } catch (e: Exception) { logError(e) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt index 732a95cc..e5e885cb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/MapResourcePatch.kt @@ -22,7 +22,7 @@ open class MapResourcePatch(private val original: Resources): ResourcePatch(orig mapping.getMappingKA(id, null) ?: super.getLayout(id) override fun getDrawable(id: Int, theme: Theme?): Drawable = - mapping.getMappingKA(id, theme) ?: super.getDrawable(id, theme) + mapping.getMappingKA(id, theme) ?: super.getDrawable(id, theme) @RequiresApi(Build.VERSION_CODES.M) override fun getColorStateList(id: Int, theme: Theme?): ColorStateList = diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt index f9d8c31c..6aadfe10 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/resources/ResourcePackManager.kt @@ -7,7 +7,6 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent -import com.lagradost.cloudstream3.R typealias PatchFactory = (Resources) -> ResourcePatch @@ -38,20 +37,18 @@ object ResourcePackManager { pluginMapping[pluginId]?.forEach { packs.remove(it) if (activePackId == it) // if pack is being removed make sure its not set - selectPack(null, AcraApplication.context?.getActivity() as? ResourcePatchActivity) + selectPack(null) } pluginMapping.remove(pluginId) } - fun selectPack(packId: String?, activity: ResourcePatchActivity?) { - if (packId == null) activePackId = null - else if (packs.containsKey(packId)) activePackId = packId - else activePackId = null + fun selectPack(packId: String?) { + activePackId = if (packId == null) null + else if (packs.containsKey(packId)) packId + else null Log.d(KEY, "Selecting: ${activePackId ?: "none"}") setKey(SETTINGS_KEY, activePackId) - - activity?.reloadResourcePatch() } @@ -59,8 +56,6 @@ object ResourcePackManager { if (!successful) return activePackId = getKey(SETTINGS_KEY) Log.d(KEY, "Selecting saved: ${activePackId ?: "none"}") - val activity = AcraApplication.context?.getActivity() as? ResourcePatchActivity - activity?.reloadResourcePatch() } } \ No newline at end of file From 61fb302a373814380257eac5f0e03d2d96a3d99f Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sun, 9 Oct 2022 01:36:06 +0200 Subject: [PATCH 003/273] delay mainpage --- .../com/lagradost/cloudstream3/MainAPI.kt | 11 +++++++- .../cloudstream3/ui/APIRepository.kt | 27 +++++++++++++++++-- .../cloudstream3/ui/home/HomeViewModel.kt | 2 ++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 47afbc42..416a7238 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -381,7 +381,16 @@ abstract class MainAPI { open var storedCredentials: String? = null open var canBeOverridden: Boolean = true - //open val uniqueId : Int by lazy { this.name.hashCode() } // in case of duplicate providers you can have a shared id + /** if this is turned on then it will request the homepage one after the other, + used to delay if they block many request at the same time*/ + open var sequentialMainPage : Boolean = false + /** in milliseconds, this can be used to add more delay between homepage requests + * on first load if sequentialMainPage is turned on */ + open var sequentialMainPageDelay : Long = 0L + /** in milliseconds, this can be used to add more delay between homepage requests when scrolling */ + open var sequentialMainPageScrollDelay : Long = 0L + /** used to keep track when last homepage request was in unixtime ms */ + var lastHomepageRequest : Long = 0L open var lang = "en" // ISO_639_1 check SubtitleHelper diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index 34cb262c..b43b1434 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -1,10 +1,12 @@ package com.lagradost.cloudstream3.ui import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.ExtractorLink +import kotlinx.coroutines.delay class APIRepository(val api: MainAPI) { companion object { @@ -62,12 +64,33 @@ class APIRepository(val api: MainAPI) { } } + suspend fun waitForHomeDelay() { + val delta = api.sequentialMainPageScrollDelay + api.lastHomepageRequest - unixTimeMS + if(delta < 0) return + delay(delta) + } + suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource> { return safeApiCall { + api.lastHomepageRequest = unixTimeMS + nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data -> listOf(api.getMainPage(page, MainPageRequest(data.name, data.data))) - } ?: api.mainPage.apmap { data -> - api.getMainPage(page, MainPageRequest(data.name, data.data)) + } ?: run { + if (api.sequentialMainPage) { + var first = true + api.mainPage.map { data -> + if (!first) // dont want to sleep on first request + delay(api.sequentialMainPageDelay) + first = false + + api.getMainPage(page, MainPageRequest(data.name, data.data)) + } + } else { + api.mainPage.apmap { data -> + api.getMainPage(page, MainPageRequest(data.name, data.data)) + } + } } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index d8497876..30fd45c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -162,6 +162,8 @@ class HomeViewModel : ViewModel() { lock += name repo?.apply { + waitForHomeDelay() + expandable[name]?.let { current -> debugAssert({ !current.hasNext }) { "Expand called when not needed" From e2118c3271ef29dd7f1ebe3b30eea3fb12c60bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samet=20Mert=20Karata=C5=9F?= <107170849+samertkaratas@users.noreply.github.com> Date: Sun, 9 Oct 2022 00:35:19 +0000 Subject: [PATCH 004/273] Update Turkish translation (#143) * Update strings.xml and array.xml * Fix escape characters * Little changes --- app/src/main/res/values-tr/array.xml | 46 ++++++++++- app/src/main/res/values-tr/strings.xml | 105 ++++++++++++++++++++++++- 2 files changed, 145 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml index 177be03b..dca01736 100644 --- a/app/src/main/res/values-tr/array.xml +++ b/app/src/main/res/values-tr/array.xml @@ -14,6 +14,41 @@ @id/cast_button_type_forward_30_seconds + + @string/none + Google + Cloudflare + + AdGuard + DNS.WATCH + Quad9 + + + 0 + 1 + 2 + + 4 + 5 + 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 @@ -210,7 +245,7 @@ Parti Pembe Material You - Material You (Secondary) + Material You (İkincil) Normal @@ -233,7 +268,6 @@ Monet2 - Koyu Gri @@ -249,6 +283,14 @@ Monet + + Çöktü + + Ok + Yavaş + Beta + + @string/automatic diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 48e36013..54b9ebea 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,6 +1,16 @@ + %d %s | %s + %s • %s + %s / %s + %s %s + +%d + -%d + %d + %d + %.1f/10.0 + %d %s Ep %d Cast: %s Bölüm %d şu tarihte yayınlanacak: @@ -14,7 +24,9 @@ Episode Poster Main Poster Next Random + @string/play_episode Go back + @string/home_change_provider_img_des Change Provider Preview Background @@ -38,6 +50,7 @@ Veri yok Daha fazla seçenek Sonraki bölüm + @string/synopsis Türler Paylaş Tarayıcıda aç @@ -70,6 +83,7 @@ İndirme başarısız oldu İndirme iptal edildi İndirme bitti + %s - %s Yayınla Bağlantılar yüklenirken hata oluştu @@ -149,11 +163,15 @@ Chromecast alt yazı ayarları Eigengravy modu - Oynatıcıya bir hız seçeneği ekler + Oynatıcıya bir hız seçeneği ekle Gözlemek için kaydır Zamanı ayarlamak için sağa veya sola kaydır Ayarları değiştirmek için kaydır Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla + + Sonraki bölümü otomatik oynat + Mevcut bölüm bittiğinde sonraki bölüme başla + Gözlemek için çift tıkla Durdurmak için çift tıkla Oynatıcı gözleme miktarı @@ -177,16 +195,18 @@ Ara Hesaplar Güncellemeler ve yedek - + Bilgi Gelişmiş arama - Sağlayıcılara göre ayrılmış arama sonuçlarını verir + Sağlayıcılara göre ayrılmış arama sonuçlarını ver Yalnızca çökmelerle ilgili verileri gönderir Hiç veri göndermez Anime için filler bölümleri gösterir Fragmanları göster Kitsu\'dan posterleri göster + Arama sonuçlarında seçilen video kalitelerini gizle + Otomatik eklenti güncellemeleri Uygulama güncellemelerini göster Başlangıçta yeni güncellemeleri otomatik olarak ara Ön sürümlere güncelle @@ -208,15 +228,19 @@ Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek Sezon + %s %d%s Sezon yok Bölüm Bölümler + %d-%d + %d %s S B Bölüm bulunamadı Dosyayı sil Sil + @string/sort_cancel Durdur Sürdür -30 @@ -251,6 +275,8 @@ OVA Asya dramaları Canlı yayınlar + NSFW + Diğerleri Film @@ -262,6 +288,8 @@ Belgesel Asya draması Canlı yayın + NSFW + Video Kaynak hatası Sunucu hatası @@ -272,7 +300,7 @@ Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla Uygulamada oynat - %s\'de oynat + %s\'de\/da oynat Tarayıcıda oynat Linki kopyala Otomatik indir @@ -284,6 +312,10 @@ Dublaj etiketi Alt yazı etiketi Başlık + show_hd_key + show_dub_key + show_sub_key + show_title_key Poster üzerindeki öğeler Güncelleme bulunamadı @@ -327,6 +359,7 @@ Yakınlaştır 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. @@ -349,7 +382,9 @@ Sağlayıcı dilleri Uygulama düzeni Tercih edilen medya + Desteklenen sağlayıcılarda NSFW\'yi etkinleştir Alt yazı kodlaması + Sağlayıcılar Düzen Otomatik @@ -364,6 +399,10 @@ + anilist_key + mal_key + opensubtitles_key + nginx_key şifre123 HavalıKullanıcıAdı hello@world.com @@ -403,6 +442,7 @@ Hepsi Maksimum Minimum + @string/none Dış hat Çökmüş Gölge @@ -464,6 +504,7 @@ Hata Alt yazılardan seçmeli alt yazıyı kaldır Alt yazılardaki şişkinliği kaldır + Tercih edilen medya diline göre filtrele Ekstralar Fragman Yayına bağlan @@ -476,5 +517,61 @@ Çökme raporları Ne izlemek istiyorsunuz? Bitti + Eklentiler + Depo ekle + Depo ismi + Depo URL\'i + Eklenti yüklendi + Eklenti silindi + %s yüklenemedi + +18 + %d %s indirilmeye başlandı + %d %s başarıyla indirildi + %s\'nin tamamı zaten indirildi + Toplu indir + eklenti + eklentiler + Bu aynı zamanda tüm depo eklentilerini de siler + Depoyu sil + Kullanmak istediğiniz sitelerin listesini indirin + İndirilen: %d + Devre dışı: %d + İndirilmeyen: %d + %d eklenti(ler) güncellendi + Site eklentilerini yüklemek için bir depo ekleyin + Topluluk depolarını görüntüle + Herkese açık liste + Tüm alt yazılar büyük harf + + Bu depodaki tüm eklentiler indirilsin mi? + %s devre dışı bırakıldı + Parçalar + Ses parçaları + Video parçaları + Yeniden başlatmada uygula + + Güvenli mod etkin + Kurtarılamaz bir çökme meydana geldi ve soruna neden olan eklentiyi bulup kaldırabilmeniz için tüm eklentileri otomatik olarak devre dışı bıraktık. + Çökme bilgisini göster + + Puan: %s + Açıklama + Versiyon + Durum + Boyut + Geliştiriciler + Desteklenen + Dil + Önce eklentiyi yükleyin + + HLS Oynatma Listesi + + Tercih edilen video oynatıcısı + Dahili oynatıcı + VLC + MPV + Web Video Yayını + Tarayıcı + Uygulama bulunamadı From b3ff3ec086f2fd7aa62d545da06218f64c65a732 Mon Sep 17 00:00:00 2001 From: Thanasis Trispiotis <79643637+thanasistrisp@users.noreply.github.com> Date: Mon, 10 Oct 2022 22:42:59 +0300 Subject: [PATCH 005/273] update strings.xml Greek (#145) * update strings.xml Add new translations to greek language (el) and fix typos. * remove duplicate --- app/src/main/res/values-el/strings.xml | 401 +++++++++++++++++++++---- 1 file changed, 348 insertions(+), 53 deletions(-) diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index de9ac4f4..11b74938 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -5,39 +5,39 @@ Αναζήτηση Λήψεις Ρυθμίσεις - Ψάξε… + Άνοιγμα με CloudStream + Αναζήτηση… Πόστερ Χωρίς δεδομένα Περισσότερες Επιλογές Πίσω - Επόμενο Επισόδειο + Επόμενο Επεισόδιο Πόστερ - Πλοκή - Genres - Μοίρασε + Κατηγορίες + Κοινοποίηση Άνοιγμα στον περιηγητή - Προσπέραση φορτώματος + Παράλειψη φόρτωσης Φόρτωση… - Watching - On-Hold - Completed - Dropped - Plan to Watch - None + Παρακολούθηση + Σε αναμονή + Ολοκληρώθηκε + Διακόπηκε + Για παρακολούθηση + Τίποτα - Αναπαραγωγή Ταινείας + Αναπαραγωγή Ταινίας Μετάδοση Torrent Πηγές Υπότιτλοι - Ξανά φόρτωσε… + Προσπάθεια επανασύνδεσης… Πίσω Πόστερ - Αναπαραγωγή Επισοδείου - - Λήξη - Σφάλμα φόρτωσεις συνδέσμων - Εσωτερικός χώρος + Αναπαραγωγή Επεισοδίου + + Λήψη + Σφάλμα φόρτωσης συνδέσμων + Εσωτερικός χώρος αποθήκευσης Dub @@ -50,77 +50,372 @@ Λυπούμαστε, η εφαρμογή κατέρρευσε. Μια ανώνυμη αναφορά σφαλμάτων θα σταλεί στους προγραμματιστές Απενεργοποιήστε την αυτόματη αναφορά σφαλμάτων + Εμφάνιση logcat 🐈 Παραπάνω πληροφορίες - Κρύψιμο + Απόκρυψη Κύριο Πόστερ Αναπαραγωγή Πληροφορίες Next Random - Change Provider - Filter Bookmarks - Bookmarks - Remove - Αναπαραγωγή Episode + Αλλαγή Παρόχου + Φιλτράρισμα Σελιδοδεικτών + Σελιδοδείκτες + Αφαίρεση + Αναπαραγωγή Επεισοδίου Υποβολή Ακύρωση Ταχύτητα αναπαραγωγής Ρυθμίσεις υπότιτλων - Χρώμα κείμενου + Χρώμα κειμένου Χρώμα περιγράμματος Χρώμα φόντου Χρώμα παραθύρου Τύπος άκρων Ύψωση υπότιτλων - Επαναφορά στην προεπιλεγμένη τιμή + Επαναφορά στις προεπιλεγμένες τιμές Προεπισκόπηση φόντου Γραμματοσειρά - Αναζήτηση με τους παρόχους - Αναζήτηση με τύπους - %d Benenes given to devs - No Benenes given + Αναζήτηση βάσει παρόχων + Αναζήτηση βάσει τύπων + %d μπανάνες δόθηκαν στους προγραμματιστές + Καμία μπανάνα δεν δόθηκε Αυτόματη επιλογή γλώσσας Λήψη γλωσσών - Κρατήστε πατημένο για επαναφορά στα προεπιλεγμένα + Κρατήστε πατημένο για επαναφορά στις προεπιλεγμένες τιμές Συνέχεια Παρακολούθησης Αφαίρεση - Παραπάνω Πληροφορίες + Επιπλέον Πληροφορίες - A VPN might be needed for this provider to work correctly - This providers is a torrent, a VPN is recommended + Η χρήση ενός VPN ίσως χρειαστεί για την ομαλή λειτουργία του τρέχοντος παρόχου + Πρόκειται για torrent, η χρήση ενός VPN συνιστάται Περιγραφή Δεν βρέθηκε περιγραφή Δεν βρέθηκε περιγραφή - Picture-in-picture - Συνεχίζει την αναπαραγωγή σε ένα μίνι παίκτη πάνω από άλλες εφαρμογές - Αλλαγή μεγέθους παίκτη + Εικόνα-σε-Εικόνα + Συνεχίζει την αναπαραγωγή σε ένα μίνι παράθυρο πάνω από άλλες εφαρμογές + Αλλαγή μεγέθους παραθύρου Αφαίρεση μαύρων περιγραμμάτων Υπότιτλοι - Ρυθμίσεις υποτίτλων του παίκτη + Ρυθμίσεις υποτίτλων του προγράμματος αναπαραγωγής + Chromecast Υπότιτλοι + Ρυθμίσεις Chromecast υποτίτλων Eigengrau Mode - Προσθέτει την επιλογή ταχύτητας στον παίκτη - Σύρετε για seek - Σύρετε αριστερά ή δεξιά για να ελέγξετε τον χρόνο στον παίκτη + Προσθέτει την επιλογή ταχύτητας στο πρόγραμμα αναπαραγωγής + Σύρετε για αναζήτηση + Σύρετε αριστερά ή δεξιά για να ελέγξετε τον χρόνο στην κάτω μπάρα Σύρετε για να αλλάξετε ρυθμίσεις Σύρετε αριστερά ή δεξιά για να αλλάξετε τη φωτεινότητα ή την ένταση - Διπλό πάτημα για seek - Διπλό πάτημα στα αριστερά ή δεξιά για seek μπροστά ή πίσω + Διπλό πάτημα για αναζήτηση + Διπλό πάτημα στα αριστερά ή δεξιά για αναζήτηση μπροστά ή πίσω Αναζήτηση - Πληροφορείες + Πληροφορίες Προχωρημένη Αναζήτηση - Δίνει τα αποτελέσματα αναζήτησης χωρισμένα ανά πάροχο + Δίνει τα αποτελέσματα αναζήτησης ταξινομημένα ανά πάροχο Αποστέλλει δεδομένα μόνο για σφάλματα Δεν στέλνει δεδομένα Εμφάνιση ενημερώσεων Αυτόματη αναζήτηση νέων ενημερώσεων - Ενημέρωση σε προ-εκδόσεις - Αναζητήστε ενημερώσεις προ-εκδόσεων αντί για κανονικές εκδόσεις + Ενημέρωση σε προ-εκδόσεις (beta) + Αναζητήστε ενημερώσεις προ-εκδόσεων (beta) αντί για σταθερές εκδόσεις Github - Light novel app by the same devs - Anime app by the same devs - Join Discord - Δώσε benene στους devs - Βenene δώθηκε + Ελαφριά novel εφαρμογή από τους ίδιους προγραμματιστές + Anime εφαρμογή από τους ίδιους προγραμματιστές + Εγγραφείτε στο Discord + Δώστε μπανάνα στους προγραμματιστές + Μπανάνα δόθηκε δώθηκε + + Ταχύτητα (%.2fx) + Βαθμολογία: %.1f + Νέα ενημέρωση διαθέσιμη!\n%s -> %s + + Πάτημα στη μέση για παύση + Χρήση φωτεινότητας συστήματος + Χρήση φωτεινότητας συστήματος στο ενσωματωμένο πρόγραμμα αναπαραγωγής αντί να εφαρμοστεί το προεπιλεγμένο σκούρο επικάλυμμα + + + Ενημέρωση προόδου παρακολούθησης + Αυτόματος συγχρονισμός της προόδου του τρέχοντος επεισοδίου + + Επαναφορά δεδομένων από αντίγραφο ασφαλείας + + Αντίγραφα ασφαλείας + Τα αντίγραφα ασφαλείας φορτώθηκαν + Η επαναφορά αντιγράφων ασφαλαείας απέτυχε από το αρχείο %s + Επιτυχής αποθήκευση δεδομένων + Δεν έχει δοθεί άδεια για πρόσβαση στον αποθηκευτικό χώρο, προσπαθήστε ξανά + Σφάλμα δημιουργίας αντιγράφων ασφαλείας %s + + Λογαριασμοί + Ενημερώσεις και αντίγραφα ασφαλείας + + Εμφάνιση filler επεισοδίου για anime + Εμφάνιση trailers + Εμφάνιση posters από kitsu + Απόκρυψη επιλεγμένης ποιότητας βίντεο στα αποτελέσματα αναζήτησης + + App Language + + Αυτός ο πάροχος δεν έχει υποστήριξη Chromecast + Δεν βρέθηκαν διαθέσιμοι σύνδεσμοι + Ο σύνδεσμος αντιγράφηκε στο πρόχειρο + + Season + %s %d%s + No Season + Episode + Episodes + %d-%d + %d %s + S + E + No Episodes found + + Διαγραφή αρχείου + Διαγραφή + Πάυση + Συνέχιση + Αυτό θα διαγράψει μόνιμα το %s\nΕπιβεβαίωση; + %dm\nαπομένουν + + Σε εξέλιξη + Κατάσταση + Έτος + Διάρκεια + Ιστότοπος + Περίληψη + + προστέθηκε στην ουρά + Δεν υπάρχουν διαθέσιμοι υπότιτλοι + Προεπιλεγμένοι υπότιτλοι + + Ελέυθερος + Σε χρήση + Εφαρμογή + + Ταινίες + Τηλεοπτικές Σειρές + Κινούμενα σχέδια + Torrents + Ντοκιμαντέρ + Ασιατικά Δράμα + Ζωντανές ροές + Άλλα + + Ταινία + Σειρά + Cartoon + Ντοκιμαντέρ + Ασιατικό Δράμα + Ζωντανή ροή + Άλλο + + Σφάλμα πηγής + Απομακρυσμένο σφάλμα + Σφάλμα απόδοσης + Μη αναμενόμενο σφάλμα αναπαραγωγής + Σφάλμα λήψης, επιβεβαιώστε ότι η άδεια αποθήκευσης είναι ενεργοποιημένη + + Chromecast επεισόδο + Αναπαραγωγή εντός της εφαρμογής + Αναπαραγωγή σε %s + Αναπαραγωγή στον περιηγητή + Αντιγραφή συνδέσμου + Αυτόματη λήψη + Λήψη mirror + Επαναφόρτωση συνδέσμων + Λήψη υποτίτλων + + Ποιότητα + Dub + Sub + Τίτλος + Εναλλαγή των στοιχείων UI στο poster + + Κλείδωμα + Αλλαγή μεγέθους + Πηγή + Παράλειψη OP + + Να μην εμφανιστεί ξανά + Παράλειψη της τρέχουσας ενημέρωσης + Ενημέρωση + Προτίμηση ποιότητας παρακολούθησης + Μέγιστοι χαρακτήρες για τίτλο + Ανάλυση αναπαραγωγής βίντεο + + Μέγεθος buffer βίντεο + Μήκος buffer βίντεο + Προσωρινή μνήμη βίντεο στο δίσκο + Εκκαθάριση προσωρινής μνήμης βίντεο και εικόνων + + Θα προκαλέσει τυχαία σφάλματα εάν οριστεί πολύ ψηλά. Μην το αλλάξετε εάν έχετε χαμηλή ποσότητα μνήμης ram, όπως σε Android TV ή παλιό τηλέφωνο + Μπορεί να προκαλέσει προβλήματα σε συστήματα με χαμηλό αποθηκευτικό χώρο, όπως σε συσκευές Android TV, εάν τον ρυθμίσετε πολύ ψηλά + + Χρήσιμο για παράκαμψη μπλοκ ISP + + Αντίγραφο ιστοτόπου + Αφαίρεση ιστοτόπου + Προσθήκη αντιγράφου ενός υπάρχοντος ιστοτόπου, με έναν διαφορετικό σύνδεσμο + + Διαδρομή λήψης + + Εμφάνιση Dubbed/Subbed Anime + + Προσαρμογή στην οθόνη + Τέντωμα + Μεγέθυνση + + Αποποίηση ευθυνών + + Γενικά + Τυχαίο κουμπί + Εμφάνιση τυχαίου κουμπιού στην Αρχική οθόνη + Γλώσσες παρόχων + Διάταξη εφαρμογής + Προτιμώμενα μέσα + Ενεργοποίηση NSFW σε υποστηριζόμενους παρόχους + Κωδικοποίηση υποτίτλων + Πάροχοι + Διάταξη + + Αυτόματο + Διάταξη TV + Διάταξη τηλεφώνου + Διάταξη emulator + + Πρωτεύον χρώμα + Θέμα εφαρμογής + Τοποθεσία τίτλου Poster + Τοποθετήστε τον τίτλο κάτω από το poster + + Κωδικός γλώσσας (el) + + Λογαριασμός + Αποσύνδεση + Σύνδεση + Εναλλαγή λογαριασμού + Προσθήκη λογαριασμού + Δημιουργία λογαριασμού + Προσθήκη παρακολούθησης + Προστέθηκε %s + Συγχρονισμός + Βαθμολογήθηκε + Πιστοποιήθηκε %s + Αποτυχία πιστοποίησης σε %s + + Τίποτα + Κανονικά + Όλα + Μέγιστο + Ελάχιστο + Περίγραμμα + Σε κατάθλιψη + Σκιά + Ανεβασμένοι + Συγχρονισμός υποτίτλων + 1000ms + Καθυστέρηση υποτίτλων + Χρησιμοποιήστε αυτό αν οι υπότιτλοι εμφανίζονται %dms πολύ νωρίς + Χρησιμοποιήστε αυτό αν οι υπότιτλοι εμφανίζονται %dms πολύ αργά + Καμία καθυστέρηση υποτίτλων + + Συνιστώμενο + Φόρτωση %s + Φόρτωση από αρχείο + Φόρτωση από το Ίντερνετ + Λήψη αρχείου + Κύριο + Υποστηρίζεται + Φόντο + + Πηγή + Τυχαίο + + Έρχεται σύντομα… + + + Εικόνα Poster + Πρόγραμμα αναπαραγωγής + Ανάλυση και τίτλος + Τίτλος + Ανάλυση + Μη έγκυρο id + Μη έγκυρα δεδομένα + Μη έγκυρος σύνδεσμος + Σφάλμα + Αφαίρεση closed captions (για άτομα με προβλήματα ακοής) από τους υπότιτλους + Αφαίρεση bloat από τους υπότιτλους + Φιλτράρισμα ανά την προτεινόμενη γλώσσα του μέσου + Έξτρα + Τρέιλερ + Σύνδεσμος για stream + Παραπομπή + Επόμενο + Παρακολούθηση βίντεο σε αυτή την γλώσσα + Προηγούμενο + Παράλειψη διαμόρφωσης + Αλλαγή της εμφάνισης της συσκευής για να ταιριάζει με την συσκευή σας + Αναφορά κατάρρευσης + Τι θα θέλατε να δείτε + Έγινε + Πρόσθετα + Προσθήκη αποθετηρίου + Όνομα αποθετηρίου + Σύνδεσμος αποθετηρίου + Το πρόσθετο φορτώθηκε + Το πρόσθετο διαγράφηκε + Απέτυχε να φορτωθεί το %s + Ξεκίνησε η λήψη %d %s + Κατέβηκε το %d %s επιτυχώς + Όλα τα %s έχουν ήδη κατέβει + Μαζική λήψη + Πρόσθετο + Πρόσθετα + Αυτό θα διαγράψει όλα τα πρόσθετα του αποθετηρίου + Διαγραφή αποθετηρίου + Λήψη της λίστας των ιστοσελίδων που θέλετε να δείτε + Κατέβηκε: %d + Απενεργοποιήθηκε: %d + Δεν κατέβηκε: %d + Ενημερώθηκαν %d πρόσθετα + Προσθήκη ενός αποθετηρίου για να εγκαταστήσετε πρόσθετα ιστοσελίδας + Προβολή αποθετηρίων κοινότητας + Δημόσια λίστα + Κεφαλοποίηση υποτίτλων + + Λήψη όλων των προσθέτων από αυτό το αποθετήριο; + %s (Απενεργοποιήθηκε) + Κομμάτια + Ηχητικά κομμάτια + Κομμάτια βίντεο + Εφαρμογή στην επανεκκίνηση + + Η ασφαλής λειτουργία ενεργοποιήθηκε + Ένα μη αντιστρέψιμο σφάλμα συνέβη και απενεργοποιήσαμε όλα τα πρόσθετα, ώστε να μπορέσετε να διαπιστώσετε ποιο πρόσθετο προκάλεσε αυτή τη κατάρρευση. + Προβολή πληροφορίας κατάρρευσης + + Βαθμολογία: %s + Περιγραφή + Έκδοση + Κατάσταση + Μέγεθος + Συγγραφείς + Υποστηρίζονται + Γλώσσα + Εγκατάσταση προσθέτου πρώτα + + HLS Playlist + + Προτεινόμενο πρόγραμμα αναπαραγωγής + Ενσωματωμένο πρόγραμμα αναπαραγωγής + VLC + MPV + Web Video Cast + Περιηγητής + Η εφαρμογή δεν βρέθηκε + + From 98ef6a3f160d73f66095643892858a24f4ed43ce Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Tue, 11 Oct 2022 02:43:56 +0700 Subject: [PATCH 006/273] added Vidmoly & Voe (extractor) (#147) --- .../cloudstream3/extractors/Vidmoly.kt | 69 +++++++++++++++++++ .../lagradost/cloudstream3/extractors/Voe.kt | 32 +++++++++ .../cloudstream3/utils/ExtractorApi.kt | 30 ++++++-- 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt new file mode 100644 index 00000000..615cfd74 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidmoly.kt @@ -0,0 +1,69 @@ +package com.lagradost.cloudstream3.extractors + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson + +class Vidmolyme : Vidmoly() { + override val mainUrl = "https://vidmoly.me" +} + +open class Vidmoly : ExtractorApi() { + override val name = "Vidmoly" + override val mainUrl = "https://vidmoly.to" + override val requiresReferer = true + + private fun String.addMarks(str: String): String { + return this.replace(Regex("\"?$str\"?"), "\"$str\"") + } + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + + val script = app.get( + url, + referer = referer, + ).document.select("script") + .find { it.data().contains("sources:") }?.data() + val videoData = script?.substringAfter("sources: [") + ?.substringBefore("],")?.addMarks("file") + val subData = script?.substringAfter("tracks: [")?.substringBefore("]")?.addMarks("file") + ?.addMarks("label")?.addMarks("kind") + + tryParseJson(videoData)?.file?.let { m3uLink -> + M3u8Helper.generateM3u8( + name, + m3uLink, + "$mainUrl/" + ).forEach(callback) + } + + tryParseJson>("[${subData}]") + ?.filter { it.kind == "captions" }?.map { + subtitleCallback.invoke( + SubtitleFile( + it.label.toString(), + fixUrl(it.file.toString()) + ) + ) + } + + } + + private data class Source( + @JsonProperty("file") val file: String? = null, + ) + + private data class SubSource( + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, + @JsonProperty("kind") val kind: String? = null, + ) + +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt new file mode 100644 index 00000000..12a76a9b --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Voe.kt @@ -0,0 +1,32 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper + +open class Voe : ExtractorApi() { + override val name = "Voe" + override val mainUrl = "https://voe.sx" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val res = app.get(url, referer = referer).document + val link = res.select("script").find { it.data().contains("const sources") }?.data() + ?.substringAfter("\"hls\": \"")?.substringBefore("\",") + + M3u8Helper.generateM3u8( + name, + link ?: return, + "$mainUrl/", + headers = mapOf("Origin" to "$mainUrl/") + ).forEach(callback) + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 199f0398..ebaaa12b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -1,10 +1,7 @@ package com.lagradost.cloudstream3.utils import android.net.Uri -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.TvType -import com.lagradost.cloudstream3.USER_AGENT -import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.extractors.* import kotlinx.coroutines.delay @@ -323,6 +320,9 @@ val extractorApis: MutableList = arrayListOf( Embedgram(), Mvidoo(), Streamplay(), + Vidmoly(), + Vidmolyme(), + Voe(), Gdriveplayerapi(), Gdriveplayerapp(), @@ -399,6 +399,28 @@ suspend fun getPostForm(requestUrl: String, html: String): String? { ).text } +fun ExtractorApi.fixUrl(url: String): String { + if (url.startsWith("http") || + // Do not fix JSON objects when passed as urls. + url.startsWith("{\"") + ) { + return url + } + if (url.isEmpty()) { + return "" + } + + val startsWithNoHttp = url.startsWith("//") + if (startsWithNoHttp) { + return "https:$url" + } else { + if (url.startsWith('/')) { + return mainUrl + url + } + return "$mainUrl/$url" + } +} + abstract class ExtractorApi { abstract val name: String abstract val mainUrl: String From a43e950a488637873553d4a46f49a8bb80a86542 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:51:03 +0200 Subject: [PATCH 007/273] Remove provider language (#141) --- .../com/lagradost/cloudstream3/MainAPI.kt | 30 ++++++++++++------- .../ui/setup/SetupFragmentExtensions.kt | 2 +- .../ui/setup/SetupFragmentLanguage.kt | 2 +- .../main/res/navigation/mobile_navigation.xml | 14 +++++++++ app/src/main/res/xml/settings_providers.xml | 1 + 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 416a7238..a06dd95e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -17,8 +17,7 @@ import com.lagradost.cloudstream3.ui.player.SubtitleData import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.loadExtractor +import com.lagradost.cloudstream3.utils.SubtitleHelper import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* @@ -191,17 +190,26 @@ object APIHolder { return list.filter { names.contains(it) }.map { DubStatus.valueOf(it) }.toHashSet() } + /** + * Gets all the activated provider languages + * Used to obey the preference provider_lang_key + * but it turned out too complicated and unnecessary with extensions. + **/ fun Context.getApiProviderLangSettings(): HashSet { - val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) - val hashSet = HashSet() - hashSet.add("en") // def is only en - val list = settingsManager.getStringSet( - this.getString(R.string.provider_lang_key), - hashSet.toMutableSet() - ) + val langs = apis.map { it.lang }.toSet() + .sortedBy { SubtitleHelper.fromTwoLettersToLanguage(it) } + return langs.toHashSet() - if (list.isNullOrEmpty()) return hashSet - return list.toHashSet() +// val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) +// val hashSet = HashSet() +// hashSet.add("en") // def is only en +// val list = settingsManager.getStringSet( +// this.getString(R.string.provider_lang_key), +// hashSet.toMutableSet() +// ) +// +// if (list.isNullOrEmpty()) return hashSet +// return list.toHashSet() } fun Context.getApiTypeSettings(): HashSet { 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 43037038..0f11f214 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 @@ -100,7 +100,7 @@ class SetupFragmentExtensions : Fragment() { next_btt?.setOnClickListener { // Continue setup if (isSetup) - findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_provider_languages) + findNavController().navigate(R.id.action_navigation_setup_extensions_to_navigation_setup_media) else findNavController().navigate(R.id.navigation_home) } 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 71472328..f9268d77 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 @@ -85,7 +85,7 @@ class SetupFragmentLanguage : Fragment() { && PluginManager.getPluginsLocal().isEmpty() //&& PREBUILT_REPOSITORIES.isNotEmpty() ) R.id.action_navigation_global_to_navigation_setup_extensions - else R.id.action_navigation_setup_language_to_navigation_setup_provider_languages + else R.id.action_navigation_setup_language_to_navigation_setup_media findNavController().navigate( nextDestination, diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 3c45ee70..94d489fd 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -522,6 +522,13 @@ app:exitAnim="@anim/exit_anim" app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + From fc7e39e3ccd0a18ec2cf95a445d3f481a58b3473 Mon Sep 17 00:00:00 2001 From: SANCTI-afk <63229113+SANCTI-afk@users.noreply.github.com> Date: Mon, 10 Oct 2022 21:58:28 +0200 Subject: [PATCH 008/273] arabicLanguage (#118) * arabicLanguage100% * Update strings.xml * Arabic Full * translated(preffVplayerBtn) --- app/src/main/res/values-ar/strings.xml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3fb2e26c..edac2760 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -13,16 +13,16 @@ سرعة (%.2fx) - Rated: %.1f + تقييم: %.1f - !تم إيجاد تحديث جديد\n%s -> %s + !تم العثور علي تحديث جديد\n%s -> %s %d دقيقة CloudStream تشغيل بواسطة CloudStream الصفحة الرئيسية البحث - التحميلات + التنزيلات الإعدادات …بحث @@ -210,14 +210,14 @@ لا موسم حلقة حلقات - ح - م + م + ح لم يتم العثور على أي حلقات حذف الملف حذف إيقاف مؤقت - أكمل + إستئناف -٣٠ +٣٠ سوف يتم الحذف نهائيا %s\nهل أنت متأكد? @@ -292,7 +292,7 @@ show_title_key التحكم في عناصر الواجهة علي الملصق - لم يتم العثور على تحديث + لم يتم العثور على تحديثات تحقق من التحديثات قفل @@ -404,7 +404,7 @@ إضافة تتبع تم إضافة %s مزامنة - مقيّم + تقييم %d / 10 /?? /%d @@ -538,4 +538,5 @@ اللغة قائمة HLS + مُشغل الفيديو المفضل From a565319ecb60d3e1f8519619776337126ad92161 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Mon, 10 Oct 2022 23:57:50 +0200 Subject: [PATCH 009/273] Remove links to website in app & fix plugin deletion bug --- .../settings/extensions/ExtensionsFragment.kt | 41 ++++++++--------- .../settings/extensions/PluginsViewModel.kt | 44 ++++++++++++++----- .../ui/setup/SetupFragmentExtensions.kt | 24 +++++----- app/src/main/res/layout/add_repo_input.xml | 19 ++++---- .../main/res/layout/fragment_extensions.xml | 12 ++--- .../res/layout/fragment_setup_extensions.xml | 22 +++++----- app/src/main/res/values/strings.xml | 2 +- 7 files changed, 89 insertions(+), 75 deletions(-) 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 8480c94d..f674cafe 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 @@ -16,7 +16,6 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController -import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent import com.lagradost.cloudstream3.R @@ -25,7 +24,6 @@ import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.utils.AppUtils.downloadAllPluginsDialog import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -34,7 +32,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.widget.LinearRecycleViewLayoutManager import kotlinx.android.synthetic.main.add_repo_input.* import kotlinx.android.synthetic.main.fragment_extensions.* -import kotlinx.android.synthetic.main.fragment_extensions.list_repositories const val PUBLIC_REPOSITORIES_LIST = "https://recloudstream.github.io/repos/" @@ -128,20 +125,20 @@ class ExtensionsFragment : Fragment() { } } - list_repositories?.setOnClickListener { - // Open webview on tv if browser fails - val isTv = isTvSettings() - openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this) - - // Set clipboard on TV because the browser might not exist or work properly - if (isTv) { - val serviceClipboard = - (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) - ?: return@setOnClickListener - val clip = ClipData.newPlainText("Repository url", PUBLIC_REPOSITORIES_LIST) - serviceClipboard.setPrimaryClip(clip) - } - } +// list_repositories?.setOnClickListener { +// // Open webview on tv if browser fails +// val isTv = isTvSettings() +// openBrowser(PUBLIC_REPOSITORIES_LIST, isTv, this) +// +// // Set clipboard on TV because the browser might not exist or work properly +// if (isTv) { +// val serviceClipboard = +// (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) +// ?: return@setOnClickListener +// val clip = ClipData.newPlainText("Repository url", PUBLIC_REPOSITORIES_LIST) +// serviceClipboard.setPrimaryClip(clip) +// } +// } observe(extensionViewModel.pluginStats) { when (it) { @@ -200,11 +197,11 @@ class ExtensionsFragment : Fragment() { } } - dialog.list_repositories?.setOnClickListener { - // Open webview on tv if browser fails - openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) - dialog.dismissSafe() - } +// dialog.list_repositories?.setOnClickListener { +// // Open webview on tv if browser fails +// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) +// dialog.dismissSafe() +// } // dialog.text2?.text = provider.name dialog.apply_btt?.setOnClickListener secondListener@{ 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 6d94f91e..b0e253be 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 @@ -12,7 +12,6 @@ import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.mvvm.launchSafe -import com.lagradost.cloudstream3.plugins.PluginData import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath import com.lagradost.cloudstream3.plugins.RepositoryManager @@ -21,8 +20,8 @@ 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.Coroutines.runOnMainThread -import kotlinx.coroutines.launch import me.xdrop.fuzzywuzzy.FuzzySearch +import java.io.File typealias Plugin = Pair /** @@ -47,7 +46,11 @@ class PluginsViewModel : ViewModel() { private val repositoryCache: MutableMap> = mutableMapOf() const val TAG = "PLG" - private fun isDownloaded(context: Context, pluginName: String, repositoryUrl: String): Boolean { + private fun isDownloaded( + context: Context, + pluginName: String, + repositoryUrl: String + ): Boolean { return getPluginPath(context, pluginName, repositoryUrl).exists() } @@ -73,7 +76,13 @@ class PluginsViewModel : ViewModel() { if (activity == null) return@ioSafe val plugins = getPlugins(repositoryUrl) - plugins.filter { plugin -> !isDownloaded(activity, plugin.second.internalName, repositoryUrl) }.also { list -> + plugins.filter { plugin -> + !isDownloaded( + activity, + plugin.second.internalName, + repositoryUrl + ) + }.also { list -> main { showToast( activity, @@ -133,9 +142,13 @@ class PluginsViewModel : ViewModel() { if (activity == null) return@ioSafe val (repo, metadata) = plugin - val file = getPluginPath(activity, plugin.second.internalName, plugin.first) + val file = if (isLocal) File(plugin.second.url) else getPluginPath( + activity, + plugin.second.internalName, + plugin.first + ) - val (success, message) = if (file.exists() || isLocal) { + val (success, message) = if (file.exists()) { PluginManager.deletePlugin(file) to R.string.plugin_deleted } else { PluginManager.downloadAndLoadPlugin( @@ -167,7 +180,9 @@ class PluginsViewModel : ViewModel() { } this.plugins = list - _filteredPlugins.postValue(false to list.filterTvTypes().filterLang().sortByQuery(currentQuery)) + _filteredPlugins.postValue( + false to list.filterTvTypes().filterLang().sortByQuery(currentQuery) + ) } // Perhaps can be optimized? @@ -175,7 +190,8 @@ class PluginsViewModel : ViewModel() { if (tvTypes.isEmpty()) return this return this.filter { (it.plugin.second.tvTypes?.any { type -> tvTypes.contains(type) } == true) || - (tvTypes.contains("Others") && (it.plugin.second.tvTypes ?: emptyList()).isEmpty()) + (tvTypes.contains("Others") && (it.plugin.second.tvTypes + ?: emptyList()).isEmpty()) } } @@ -199,7 +215,9 @@ class PluginsViewModel : ViewModel() { } fun updateFilteredPlugins() { - _filteredPlugins.postValue(false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery)) + _filteredPlugins.postValue( + false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery) + ) } fun updatePluginList(context: Context?, repositoryUrl: String) = viewModelScope.launchSafe { @@ -210,7 +228,9 @@ class PluginsViewModel : ViewModel() { fun search(query: String?) { currentQuery = query - _filteredPlugins.postValue(true to (filteredPlugins.value?.second?.sortByQuery(query) ?: emptyList())) + _filteredPlugins.postValue( + true to (filteredPlugins.value?.second?.sortByQuery(query) ?: emptyList()) + ) } /** @@ -226,6 +246,8 @@ class PluginsViewModel : ViewModel() { } plugins = downloadedPlugins - _filteredPlugins.postValue(false to downloadedPlugins.filterTvTypes().filterLang().sortByQuery(currentQuery)) + _filteredPlugins.postValue( + false to downloadedPlugins.filterTvTypes().filterLang().sortByQuery(currentQuery) + ) } } \ No newline at end of file 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 43037038..7b7e5458 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 @@ -7,21 +7,16 @@ import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController -import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.MainActivity.Companion.afterRepositoryLoadedEvent import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.plugins.RepositoryManager import com.lagradost.cloudstream3.plugins.RepositoryManager.PREBUILT_REPOSITORIES -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings -import com.lagradost.cloudstream3.ui.settings.extensions.PUBLIC_REPOSITORIES_LIST import com.lagradost.cloudstream3.ui.settings.extensions.PluginsViewModel import com.lagradost.cloudstream3.ui.settings.extensions.RepoAdapter import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import kotlinx.android.synthetic.main.fragment_extensions.blank_repo_screen -import kotlinx.android.synthetic.main.fragment_extensions.list_repositories import kotlinx.android.synthetic.main.fragment_extensions.repo_recycler_view -import kotlinx.android.synthetic.main.fragment_setup_extensions.* import kotlinx.android.synthetic.main.fragment_setup_media.next_btt import kotlinx.android.synthetic.main.fragment_setup_media.prev_btt import kotlinx.android.synthetic.main.fragment_setup_media.setup_root @@ -64,18 +59,19 @@ class SetupFragmentExtensions : Fragment() { val hasRepos = repositories.isNotEmpty() repo_recycler_view?.isVisible = hasRepos blank_repo_screen?.isVisible = !hasRepos - view_public_repositories_button?.isVisible = hasRepos +// view_public_repositories_button?.isVisible = hasRepos if (hasRepos) { repo_recycler_view?.adapter = RepoAdapter(true, {}, { PluginsViewModel.downloadAll(activity, it.url, null) }).apply { updateList(repositories) } - } else { - list_repositories?.setOnClickListener { - // Open webview on tv if browser fails - openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) - } } +// else { +// list_repositories?.setOnClickListener { +// // Open webview on tv if browser fails +// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) +// } +// } } } @@ -84,9 +80,9 @@ class SetupFragmentExtensions : Fragment() { context?.fixPaddingStatusbar(setup_root) val isSetup = arguments?.getBoolean(SETUP_EXTENSION_BUNDLE_IS_SETUP) ?: false - view_public_repositories_button?.setOnClickListener { - openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) - } +// view_public_repositories_button?.setOnClickListener { +// openBrowser(PUBLIC_REPOSITORIES_LIST, isTvSettings(), this) +// } with(context) { if (this == null) return diff --git a/app/src/main/res/layout/add_repo_input.xml b/app/src/main/res/layout/add_repo_input.xml index 445e71ec..6f6b4d5b 100644 --- a/app/src/main/res/layout/add_repo_input.xml +++ b/app/src/main/res/layout/add_repo_input.xml @@ -28,15 +28,15 @@ android:textSize="20sp" android:textStyle="bold" /> - + + + + + + + + + - + + + + + + - + + + + + - + + + + + + Disabled: %d Not downloaded: %d Updated %d plugins - Add a repository to install site extensions + CloudStream has no sites installed by default. You need to install the sites from repositories.\n\nBecause of a brainless DMCA takedown by Sky Uk Limited 🤮 we cannot link the repository sites in app.\n\nJoin our discord for links or search online. View community repositories Public list Uppercase all subtitles From 661f8c3c4e8b6ee72f40477d73bd8677db15174e Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Tue, 11 Oct 2022 15:24:16 +0200 Subject: [PATCH 010/273] mini api fix --- .../com/lagradost/cloudstream3/MainAPI.kt | 39 +++++++++++++++---- .../cloudstream3/ui/APIRepository.kt | 14 +++++-- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index a06dd95e..e5896434 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -22,7 +22,6 @@ import okhttp3.Interceptor import java.text.SimpleDateFormat import java.util.* import kotlin.math.absoluteValue -import kotlin.collections.MutableList const val USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" @@ -332,13 +331,24 @@ data class SettingsJson( data class MainPageData( val name: String, val data: String, + val horizontalImages: Boolean = false ) data class MainPageRequest( val name: String, val data: String, + val horizontalImages: Boolean, + //TODO genre selection or smth ) +fun mainPage(url: String, name: String, horizontalImages: Boolean = false): MainPageData { + return MainPageData(name = name, data = url, horizontalImages = horizontalImages) +} + +fun mainPageOf(vararg elements: MainPageData): List { + return elements.toList() +} + /** return list of MainPageData with url to name, make for more readable code */ fun mainPageOf(vararg elements: Pair): List { return elements.map { (url, name) -> MainPageData(name = name, data = url) } @@ -347,7 +357,7 @@ fun mainPageOf(vararg elements: Pair): List { fun newHomePageResponse( name: String, list: List, - hasNext: Boolean? = null + hasNext: Boolean? = null, ): HomePageResponse { return HomePageResponse( listOf(HomePageList(name, list)), @@ -355,6 +365,17 @@ fun newHomePageResponse( ) } +fun newHomePageResponse( + data: MainPageRequest, + list: List, + hasNext: Boolean? = null, +): HomePageResponse { + return HomePageResponse( + listOf(HomePageList(data.name, list, data.horizontalImages)), + hasNext = hasNext ?: list.isNotEmpty() + ) +} + fun newHomePageResponse(list: HomePageList, hasNext: Boolean? = null): HomePageResponse { return HomePageResponse(listOf(list), hasNext = hasNext ?: list.list.isNotEmpty()) } @@ -391,14 +412,17 @@ abstract class MainAPI { /** if this is turned on then it will request the homepage one after the other, used to delay if they block many request at the same time*/ - open var sequentialMainPage : Boolean = false + open var sequentialMainPage: Boolean = false + /** in milliseconds, this can be used to add more delay between homepage requests * on first load if sequentialMainPage is turned on */ - open var sequentialMainPageDelay : Long = 0L + open var sequentialMainPageDelay: Long = 0L + /** in milliseconds, this can be used to add more delay between homepage requests when scrolling */ - open var sequentialMainPageScrollDelay : Long = 0L + open var sequentialMainPageScrollDelay: Long = 0L + /** used to keep track when last homepage request was in unixtime ms */ - var lastHomepageRequest : Long = 0L + var lastHomepageRequest: Long = 0L open var lang = "en" // ISO_639_1 check SubtitleHelper @@ -431,7 +455,8 @@ abstract class MainAPI { open val vpnStatus = VPNStatus.None open val providerType = ProviderType.DirectProvider - open val mainPage = listOf(MainPageData("", "")) + //emptyList() // + open val mainPage = listOf(MainPageData("", "", false)) @WorkerThread open suspend fun getMainPage( diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index b43b1434..0e5e544b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -66,7 +66,7 @@ class APIRepository(val api: MainAPI) { suspend fun waitForHomeDelay() { val delta = api.sequentialMainPageScrollDelay + api.lastHomepageRequest - unixTimeMS - if(delta < 0) return + if (delta < 0) return delay(delta) } @@ -75,7 +75,7 @@ class APIRepository(val api: MainAPI) { api.lastHomepageRequest = unixTimeMS nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data -> - listOf(api.getMainPage(page, MainPageRequest(data.name, data.data))) + listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages))) } ?: run { if (api.sequentialMainPage) { var first = true @@ -84,11 +84,17 @@ class APIRepository(val api: MainAPI) { delay(api.sequentialMainPageDelay) first = false - api.getMainPage(page, MainPageRequest(data.name, data.data)) + api.getMainPage( + page, + MainPageRequest(data.name, data.data, data.horizontalImages) + ) } } else { api.mainPage.apmap { data -> - api.getMainPage(page, MainPageRequest(data.name, data.data)) + api.getMainPage( + page, + MainPageRequest(data.name, data.data, data.horizontalImages) + ) } } } From 1228701f0eddb40d0b4b1957068d26b736e8c344 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Thu, 13 Oct 2022 22:58:18 +0200 Subject: [PATCH 011/273] Fix focusing add repo on firestick --- .../ui/settings/extensions/ExtensionsFragment.kt | 5 +++++ app/src/main/res/layout/fragment_extensions.xml | 2 ++ 2 files changed, 7 insertions(+) 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 f674cafe..418519e2 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 @@ -232,6 +232,11 @@ class ExtensionsFragment : Fragment() { val isTv = isTrueTvSettings() add_repo_button?.isGone = isTv add_repo_button_imageview_holder?.isVisible = isTv + + // Band-aid for Fire TV + plugin_storage_appbar?.isFocusableInTouchMode = isTv + add_repo_button_imageview?.isFocusableInTouchMode = isTv + add_repo_button?.setOnClickListener(addRepositoryClick) add_repo_button_imageview?.setOnClickListener(addRepositoryClick) diff --git a/app/src/main/res/layout/fragment_extensions.xml b/app/src/main/res/layout/fragment_extensions.xml index 7d262bf3..c786e2bc 100644 --- a/app/src/main/res/layout/fragment_extensions.xml +++ b/app/src/main/res/layout/fragment_extensions.xml @@ -56,6 +56,7 @@ Date: Fri, 14 Oct 2022 20:18:32 +0200 Subject: [PATCH 012/273] Changed home selection sheet to fully open and allow upwards scrolling :) --- .../java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt | 2 ++ app/src/main/res/layout/home_select_mainpage.xml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) 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 520b6b99..0d571b76 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 @@ -23,6 +23,7 @@ import androidx.preference.PreferenceManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearSnapHelper import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.* @@ -280,6 +281,7 @@ class HomeFragment : Fragment() { val builder = BottomSheetDialog(this) + builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED builder.setContentView(R.layout.home_select_mainpage) builder.show() builder.let { dialog -> diff --git a/app/src/main/res/layout/home_select_mainpage.xml b/app/src/main/res/layout/home_select_mainpage.xml index ca9fa517..8546d9b1 100644 --- a/app/src/main/res/layout/home_select_mainpage.xml +++ b/app/src/main/res/layout/home_select_mainpage.xml @@ -10,7 +10,7 @@ android:id="@+id/listview1" android:layout_width="match_parent" android:layout_height="wrap_content" - + android:nestedScrollingEnabled="true" android:layout_rowWeight="1" android:layout_marginTop="10dp" android:layout_marginBottom="60dp" From e67d248f7f18f13fa67f668b85ae1c6e96350592 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Fri, 14 Oct 2022 23:56:21 +0200 Subject: [PATCH 013/273] Fix searching in repos & search provider selection bottom sheet --- .../cloudstream3/ui/search/SearchFragment.kt | 2 ++ .../ui/settings/extensions/PluginsFragment.kt | 13 ++++--------- .../ui/settings/extensions/PluginsViewModel.kt | 2 +- app/src/main/res/menu/repository.xml | 14 +++++++++----- 4 files changed, 16 insertions(+), 15 deletions(-) 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 69f8a274..5f108cd1 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 @@ -17,6 +17,7 @@ import androidx.fragment.app.activityViewModels import androidx.preference.PreferenceManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton import com.lagradost.cloudstream3.* @@ -232,6 +233,7 @@ class SearchFragment : Fragment() { val builder = BottomSheetDialog(ctx) + builder.behavior.state = BottomSheetBehavior.STATE_EXPANDED builder.setContentView(R.layout.home_select_mainpage) builder.show() builder.let { dialog -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index e4435fff..aa302c5a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -1,27 +1,22 @@ package com.lagradost.cloudstream3.ui.settings.extensions import android.os.Bundle -import android.view.* -import android.widget.SearchView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.lifecycle.map -import com.lagradost.cloudstream3.AcraApplication -import com.lagradost.cloudstream3.CommonActivity import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.appLanguages -import com.lagradost.cloudstream3.ui.settings.getCurrentLocale -import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.UIHelper.toPx -import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import kotlinx.android.synthetic.main.fragment_plugins.* const val PLUGINS_BUNDLE_NAME = "name" 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 b0e253be..536dc33b 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 @@ -210,7 +210,7 @@ class PluginsViewModel : ViewModel() { // Return list to base state if no query this.sortedBy { it.plugin.second.name } } else { - this.sortedBy { -FuzzySearch.ratio(it.plugin.second.name, query) } + this.sortedBy { -FuzzySearch.partialRatio(it.plugin.second.name.lowercase(), query.lowercase()) } } } diff --git a/app/src/main/res/menu/repository.xml b/app/src/main/res/menu/repository.xml index d44c9764..be99b1a8 100644 --- a/app/src/main/res/menu/repository.xml +++ b/app/src/main/res/menu/repository.xml @@ -1,13 +1,17 @@ - + + + android:icon="@drawable/search_icon" + android:searchIcon="@drawable/search_icon" + app:actionViewClass="androidx.appcompat.widget.SearchView" + app:showAsAction="always|collapseActionView" + tools:ignore="AppCompatResource" /> Date: Sat, 15 Oct 2022 22:01:16 +0200 Subject: [PATCH 014/273] [skip ci] remove link to site --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 1c9ca358..e7221440 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ # CloudStream **⚠️ Warning: By default this app doesn't provide any video sources, you have to install extensions in order to add functionality to the app.** -You can find the list of community-maintained extension repositories [here -](https://recloudstream.github.io/repos/) [![Discord](https://invidget.switchblade.xyz/5Hus6fM)](https://discord.gg/5Hus6fM) From 78bf779136bcc97c126a1c32933cdb4de433110f Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 16 Oct 2022 09:47:03 +0200 Subject: [PATCH 015/273] fix merge issue --- app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 409da7c6..aa64caa6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -75,8 +75,6 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.DataStoreHelper.setViewPos -import com.lagradost.cloudstream3.utils.Event -import com.lagradost.cloudstream3.utils.IOnBackPressed import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.checkWrite From f49d9de09be37e26633a8449e637a2525e8c8c48 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 16 Oct 2022 19:42:32 +0200 Subject: [PATCH 016/273] Added CineGrabber Extractor --- .../cloudstream3/extractors/GuardareStream.kt | 81 +++++++++++++++---- .../cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt index 57435161..f25cb5ba 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt @@ -1,36 +1,83 @@ package com.lagradost.cloudstream3.extractors + import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +class CineGrabber : GuardareStream() { + override var name = "CineGrabber" + override var mainUrl = "https://cinegrabber.com" +} + open class GuardareStream : ExtractorApi() { override var name = "Guardare" override var mainUrl = "https://guardare.stream" override val requiresReferer = false - data class GuardareJsonData ( - @JsonProperty("data") val data : List, + data class GuardareJsonData( + @JsonProperty("data") val data: List, + @JsonProperty("captions") val captions: List?, ) - data class GuardareData ( - @JsonProperty("file") val file : String, - @JsonProperty("label") val label : String, - @JsonProperty("type") val type : String + data class GuardareData( + @JsonProperty("file") val file: String, + @JsonProperty("label") val label: String, + @JsonProperty("type") val type: String ) - override suspend fun getUrl(url: String, referer: String?): List? { - val response = app.post(url.replace("/v/","/api/source/"), data = mapOf("d" to mainUrl)).text - val jsonvideodata = AppUtils.parseJson(response) - return jsonvideodata.data.map { - ExtractorLink( - it.file+".${it.type}", - this.name, - it.file+".${it.type}", - mainUrl, - it.label.filter{ it.isDigit() }.toInt(), - false + + // https://cinegrabber.com/asset/userdata/224879/caption/gqdmzh-71ez76z8/876438.srt + data class GuardareCaptions( + @JsonProperty("id") val id: String, + @JsonProperty("hash") val hash: String, + @JsonProperty("language") val language: String?, + @JsonProperty("extension") val extension: String + ) { + fun getUrl(mainUrl: String, userId: String): String { + return "$mainUrl/asset/userdata/$userId/caption/$hash/$id.$extension" + } + } + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val response = + app.post(url.replace("/v/", "/api/source/"), data = mapOf("d" to mainUrl)).text + + val jsonVideoData = AppUtils.parseJson(response) + jsonVideoData.data.forEach { + callback.invoke( + ExtractorLink( + it.file + ".${it.type}", + this.name, + it.file + ".${it.type}", + mainUrl, + it.label.filter { it.isDigit() }.toInt(), + false + ) ) } + + if (!jsonVideoData.captions.isNullOrEmpty()){ + val iframe = app.get(url) + // var USER_ID = '224879'; + val userIdRegex = Regex("""USER_ID.*?(\d+)""") + val userId = userIdRegex.find(iframe.text)?.groupValues?.getOrNull(1) ?: return + jsonVideoData.captions.forEach { + if (it == null) return@forEach + val subUrl = it.getUrl(mainUrl, userId) + subtitleCallback.invoke( + SubtitleFile( + it.language ?: "", + subUrl + ) + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index ebaaa12b..8d6b1b82 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -283,6 +283,7 @@ val extractorApis: MutableList = arrayListOf( Userload(), Supervideo(), GuardareStream(), + CineGrabber(), // StreamSB.kt works // SBPlay(), From a58ca547d7e85c96e7627cf9c712517400b9c55e Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 16 Oct 2022 19:51:00 +0200 Subject: [PATCH 017/273] Apply fixes from CodeFactor (#157) Co-authored-by: codefactor-io --- .../main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt | 2 +- .../main/java/com/lagradost/cloudstream3/extractors/Uqload.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt index 4a9f2f52..eddbf6df 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt @@ -26,7 +26,7 @@ open class Evoload : ExtractorApi() { } else { "" } - + val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// url } else { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt index e5d2875f..5109acc3 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt @@ -25,7 +25,7 @@ open class Uqload : ExtractorApi() { } else { "" } - + val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// url } else { From 4848e43c97dac696fc23e03b123ec0a892ce6db6 Mon Sep 17 00:00:00 2001 From: Sdarfeesh <50188628+Sdarfeesh@users.noreply.github.com> Date: Tue, 18 Oct 2022 18:33:17 +0800 Subject: [PATCH 018/273] Simplified Chinese Correction and Update (#150) --- app/src/main/res/values-zh/strings.xml | 225 +++++++++++++------------ 1 file changed, 118 insertions(+), 107 deletions(-) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a6557990..8f436fe9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -12,27 +12,27 @@ %.1f/10.0 %d %s 共 %d 集 - 演员: %s + 演员:%s 第 %d 集将发布于 %dd %dh %dm %dh %dm %dm - 海报 + 封面 @string/result_poster_img_des - 剧集海报 - 主海报 + 剧集封面 + 主封面 随机下一个 @string/play_episode 返回 @string/home_change_provider_img_des - 更改内容提供者 + 更改片源 预览背景 - 速度 (%.2fx) - 评分: %.1f + 速度(%.2fx) + 评分:%.1f 发现新版本!\n%s -> %s 填充 %d 分钟 @@ -55,7 +55,7 @@ 分享 在浏览器中打开 跳过加载 - 正在加载… + 加载中… 正在观看 暂时搁置 @@ -77,16 +77,16 @@ 下载 已下载 - 正在下载 + 下载中 下载暂停 下载开始 下载失败 下载取消 下载完毕 %s - %s - 流媒体 + 播放 - 加载链接时出错 + 加载链接错误 内部存储 配音 @@ -125,33 +125,33 @@ 字体 字体大小 - 按内容提供者搜索 + 按片源搜索 按类型搜索 - 送给开发者 %d 根香蕉 + 给开发者送 %d 根香蕉 不送香蕉 自动选择语言 下载语言 字幕语言 按住重置为默认值 - 将字体置于 %s 导入 + 将字体导入到 %s 继续观看 移除 更多信息 @string/home_play - 此内容提供者可能需要 VPN 才能正常工作 - 此内容提供者是一个种子 ,建议使用 VPN + 此片源可能需要 VPN 才能正常使用 + 此片源是种子,建议使用 VPN 站点不提供元数据,如果站点上不存在元数据,视频加载将失败。 简介 - 没有找到简介 - 没有找到简介 + 未找到简介 + 未找到简介 - 显示日志 + 显示日志 🐈 画中画 在其他应用之上的迷你播放器中继续播放 @@ -162,24 +162,24 @@ 投屏字幕 投屏字幕设置 - 本征模式 + 倍速模式 在播放器中添加播放速度选项 滑动控制进度 - 向屏幕左侧或右侧滑动来控制播放进度 + 左右滑动控制播放进度 滑动更改设置 - 在屏幕左侧或右侧滑动来更改亮度或音量 + 上下滑动修改亮度或音量 自动播放下一集 - 当前一集结束时开始下一集 + 播放完毕后播放下一集 双击控制进度 双击暂停 - 播放器快进或快退时间 - 在屏幕左侧或右侧双击来快进或快退 + 双击控制进度时间 + 在左右侧双击快进或快退 - 双击屏幕中间暂停 + 双击中间暂停 使用系统亮度 - 在应用播放器中使用系统亮度,而不是黑色遮罩 + 在应用播放器中使用系统亮度替代黑色遮罩 更新观看进度 @@ -189,48 +189,49 @@ 备份数据 已加载备份文件 - 无法从文件 %s 中还原数据 - 成功存储数据 + 无法从 %s 文件中还原数据 + 成功保存数据 缺少存储权限,请重试 - 备份 %s 时出错 + 备份 %s 出错 搜索 账户 - 更新和备份 - + 更新与备份 + 信息 高级搜索 - 给出按内容提供者分隔的搜索结果 - 只发送关于崩溃的数据 + 按片源分割搜索结果 + 仅发送关于崩溃的数据 不发送数据 - 为动画显示下一集 + 显示动画外传 显示预告片 - 显示来自 Kitsu 的海报 - 在搜索结果中隐藏选中视频质量 + 显示来自 Kitsu 的封面 + 在搜索结果中隐藏选中视频画质 自动更新插件 显示应用更新 启动时自动搜索更新 更新至预览版 - 搜索预览版更新,而不是仅搜索完整版本 + 搜索预览版更新替代仅搜索完整版本 Github - 由同一开发者开发的轻小说应用 - 由同一开发者开发的动漫应用 + 由相同开发者开发的轻小说应用 + 由相同开发者开发的动漫应用 加入 Discord - 送给开发者一根香蕉 + 给开发者送一根香蕉 送香蕉 应用语言 - 此内容提供者不支持投屏 + 此片源不支持投屏 未找到链接 - 链接已复制到剪贴板 + 连接已复制到剪贴板 播放剧集 重置为默认值 - 抱歉,应用崩溃了,将向开发人员发送一份匿名错误报告 + 抱歉,应用崩溃了,将发送一份匿名错误报告给开发者 + %s %d%s 无季 @@ -247,8 +248,8 @@ 继续 -30 +30 - 这将永久删除 %s\n确定吗? - 剩余 %dm 分钟\n + 这将永久删除 %s\n您确定吗? + 剩余 %d 分钟 连载中 @@ -256,11 +257,11 @@ 状态 年份 评分 - 持续时间 + 时间 网站 简介 - 队列 + 已加入队列 无字幕 默认 @@ -270,29 +271,29 @@ 电影 - 剧集 + 电视剧 卡通 动漫 种子 纪录片 - 原创影像动画 + OVA 亚洲剧 直播 - 工作场所不宜 - 视频 + NSFW + 其他 电影 - 剧集 + 电视剧 卡通 - 动漫 - 原创影像动画 + @string/anime + @string/ova 种子 纪录片 亚洲剧 直播 - 工作场所不宜 - 视频 + NSFW + 其他 来源错误 远程错误 @@ -308,10 +309,10 @@ 复制链接 自动下载 下载镜像 - 重新加载镜像 + 重新加载链接 下载字幕 - 质量标签 + 画质标签 配音标签 字幕标签 标题 @@ -319,7 +320,7 @@ show_dub_key show_sub_key show_title_key - 开关海报上的 UI 元素 + 封面内容 未找到更新 检查更新 @@ -327,37 +328,37 @@ 锁定 调整画面 来源 - 跳过片头曲 + 跳过片头 不再显示 跳过此更新 更新 - 首选播放质量 - 视频播放器标题最大字符数 - 视频播放器分辨率 + 首选播放画质 + 视频播放器标题最多字符 + 视频播放器标题 视频缓冲大小 - 视频缓冲时长 - 磁盘上的视频缓存 - 清除视频和图像缓存 + 视频缓冲长度 + 视频缓存存储 + 清除视频与图像缓存 - 如果设置太高,可能会在内存较低的系统(如 Android TV 设备或旧手机)上导致问题 - 如果将存储空间设置得太高,可能会导致 Android TV 设备等存储空间不足的系统出现问题 + 设置过高可能导致崩溃。如果您使用如 Android TV 或旧手机此类低内存设备,请勿修改 + 设置过高可能导致如 Android TV 此类低存储空间设备出现系统问题 DNS over HTTPS - 用于绕过 ISP 的封锁 + 防止 DNS 劫持 - 克隆网站 - 移除网站 - 添加现有站点具有不同的 URL 的克隆 + 复制片源 + 移除片源 + 添加不同链接的已有片源复制 下载路径 - Nginx 服务器 URL + Nginx 服务器链接 - 显示有配音或字幕的动画 + 显示有配音/字幕的动漫 - 适应屏幕 + 适应 拉伸 缩放 @@ -379,15 +380,15 @@ responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. - 全局 + 通用 随机按钮 在主页中显示随机按钮 - 内容提供者语言 + 片源语言 应用布局 - 首选媒体 - 在支持的内容提供者中开启工作场所不宜内容 + 首选类型 + 在支持的片源中启用 NSFW 内容 字幕编码 - 内容提供者 + 片源 布局 自动 @@ -397,8 +398,8 @@ 主题色 应用主题 - 海报标题位置 - 将标题置于海报下方 + 封面标题位置 + 将标题移至封面下方 @@ -411,7 +412,7 @@ 邮箱 IP 网站名称 - 网站 URL + 网站链接 语言代码 (zh) Date: Tue, 25 Oct 2022 10:41:12 +0200 Subject: [PATCH 023/273] [skip ci] increase issue analysis threshold --- .github/workflows/issue_action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml index 79e7766c..a81c4b42 100644 --- a/.github/workflows/issue_action.yml +++ b/.github/workflows/issue_action.yml @@ -18,7 +18,7 @@ jobs: uses: actions-cool/issues-similarity-analysis@v1 with: token: ${{ steps.generate_token.outputs.token }} - filter-threshold: 0.5 + filter-threshold: 0.60 title-excludes: '' comment-title: | ### Your issue looks similar to these issues: From 7f71eef75550661eb9b418ebea31560d457442e3 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Wed, 26 Oct 2022 14:59:29 +0200 Subject: [PATCH 024/273] Allow playback from buffer with no internet --- .../cloudstream3/ui/player/CS3IPlayer.kt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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 ea466120..a838c85c 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 @@ -17,10 +17,7 @@ import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.trackselection.DefaultTrackSelector import com.google.android.exoplayer2.trackselection.TrackSelector import com.google.android.exoplayer2.ui.SubtitleView -import com.google.android.exoplayer2.upstream.DataSource -import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource -import com.google.android.exoplayer2.upstream.HttpDataSource +import com.google.android.exoplayer2.upstream.* import com.google.android.exoplayer2.upstream.cache.CacheDataSource import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache @@ -880,7 +877,16 @@ class CS3IPlayer : IPlayer { } override fun onPlayerError(error: PlaybackException) { - playerError?.invoke(error) + // If the Network fails then ignore the exception if the duration is set. + // This is to switch mirrors automatically if the stream has not been fetched, but + // allow playing the buffer without internet as then the duration is fetched. + if (error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED + && exoPlayer?.duration != C.TIME_UNSET + ) { + exoPlayer?.prepare() + } else { + playerError?.invoke(error) + } super.onPlayerError(error) } From ecd363992c42d4c489f52191f657842a0a23eaf4 Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:38:46 +0700 Subject: [PATCH 025/273] added sub to streamsb & xtreamCdn (#163) --- .../cloudstream3/extractors/Gdriveplayer.kt | 55 ++++++++++++---- .../cloudstream3/extractors/Moviehab.kt | 40 ++++++++++++ .../cloudstream3/extractors/SpeedoStream.kt | 6 +- .../cloudstream3/extractors/StreamSB.kt | 20 +++++- .../extractors/VidSrcExtractor.kt | 8 +-- .../cloudstream3/extractors/XStreamCdn.kt | 64 ++++++++++++++----- .../cloudstream3/utils/ExtractorApi.kt | 6 ++ 7 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt index dfccc118..df9c74a4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Gdriveplayer.kt @@ -3,6 +3,7 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import org.jsoup.nodes.Element import java.security.DigestException import java.security.MessageDigest @@ -10,43 +11,47 @@ import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec +class DatabaseGdrive2 : Gdriveplayer() { + override var mainUrl = "https://databasegdriveplayer.co" +} + class DatabaseGdrive : Gdriveplayer() { override var mainUrl = "https://series.databasegdriveplayer.co" } -class Gdriveplayerapi: Gdriveplayer() { +class Gdriveplayerapi : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayerapi.com" } -class Gdriveplayerapp: Gdriveplayer() { +class Gdriveplayerapp : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.app" } -class Gdriveplayerfun: Gdriveplayer() { +class Gdriveplayerfun : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.fun" } -class Gdriveplayerio: Gdriveplayer() { +class Gdriveplayerio : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.io" } -class Gdriveplayerme: Gdriveplayer() { +class Gdriveplayerme : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.me" } -class Gdriveplayerbiz: Gdriveplayer() { +class Gdriveplayerbiz : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.biz" } -class Gdriveplayerorg: Gdriveplayer() { +class Gdriveplayerorg : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.org" } -class Gdriveplayerus: Gdriveplayer() { +class Gdriveplayerus : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.us" } -class Gdriveplayerco: Gdriveplayer() { +class Gdriveplayerco : Gdriveplayer() { override val mainUrl: String = "https://gdriveplayer.co" } @@ -136,6 +141,10 @@ open class Gdriveplayer : ExtractorApi() { return find(str)?.groupValues?.getOrNull(1) } + private fun String.addMarks(str: String): String { + return this.replace(Regex("\"?$str\"?"), "\"$str\"") + } + override suspend fun getUrl( url: String, referer: String?, @@ -145,18 +154,19 @@ open class Gdriveplayer : ExtractorApi() { val document = app.get(url).document val eval = unpackJs(document)?.replace("\\", "") ?: return - val data = AppUtils.tryParseJson(Regex("data='(\\S+?)'").first(eval)) ?: return + val data = tryParseJson(Regex("data='(\\S+?)'").first(eval)) ?: return val password = Regex("null,['|\"](\\w+)['|\"]").first(eval) ?.split(Regex("\\D+")) ?.joinToString("") { Char(it.toInt()).toString() }.let { Regex("var pass = \"(\\S+?)\"").first(it ?: return)?.toByteArray() } ?: throw ErrorLoadingException("can't find password") - val decryptedData = - cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "") - ?.substringAfter("sources:[")?.substringBefore("],") + val decryptedData = cryptoAESHandler(data, password, false)?.let { getAndUnpack(it) }?.replace("\\", "") - Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(decryptedData ?: return).map { + val sourceData = decryptedData?.substringAfter("sources:[")?.substringBefore("],") + val subData = decryptedData?.substringAfter("tracks:[")?.substringBefore("],") + + Regex("\"file\":\"(\\S+?)\".*?res=(\\d+)").findAll(sourceData ?: return).map { it.groupValues[1] to it.groupValues[2] }.toList().distinctBy { it.second }.map { (link, quality) -> callback.invoke( @@ -171,6 +181,17 @@ open class Gdriveplayer : ExtractorApi() { ) } + subData?.addMarks("file")?.addMarks("kind")?.addMarks("label").let { dataSub -> + tryParseJson>("[$dataSub]")?.map { sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.label, + httpsify(sub.file) + ) + ) + } + } + } data class AesData( @@ -179,4 +200,10 @@ open class Gdriveplayer : ExtractorApi() { @JsonProperty("s") val s: String ) + data class Tracks( + @JsonProperty("file") val file: String, + @JsonProperty("kind") val kind: String, + @JsonProperty("label") val label: String + ) + } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt new file mode 100644 index 00000000..e2eb7bf0 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Moviehab.kt @@ -0,0 +1,40 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper + +class Moviehab : ExtractorApi() { + override var name = "Moviehab" + override var mainUrl = "https://play.moviehab.com" + override val requiresReferer = false + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val res = app.get(url) + res.document.select("video#player").let { + //should redirect first for making it works + val link = app.get("$mainUrl/${it.select("source").attr("src")}", referer = url).url + M3u8Helper.generateM3u8( + this.name, + link, + url + ).forEach(callback) + + Regex("src[\"|'],\\s[\"|'](\\S+)[\"|']\\)").find(res.text)?.groupValues?.get(1).let {sub -> + subtitleCallback.invoke( + SubtitleFile( + it.select("track").attr("label"), + "$mainUrl/$sub" + ) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt index 6153a7c1..8ef6c463 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/SpeedoStream.kt @@ -7,7 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper -class SpeedoStream : ExtractorApi() { +class SpeedoStream1 : SpeedoStream() { + override val mainUrl = "https://speedostream.nl" +} + +open class SpeedoStream : ExtractorApi() { override val name = "SpeedoStream" override val mainUrl = "https://speedostream.com" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index 30a0496d..913b410b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -7,6 +7,11 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper +class Sbspeed : StreamSB() { + override var name = "Sbspeed" + override var mainUrl = "https://sbspeed.com" +} + class Streamsss : StreamSB() { override var mainUrl = "https://streamsss.net" } @@ -93,15 +98,15 @@ open class StreamSB : ExtractorApi() { } data class Subs ( - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String, + @JsonProperty("file") val file: String? = null, + @JsonProperty("label") val label: String? = null, ) data class StreamData ( @JsonProperty("file") val file: String, @JsonProperty("cdn_img") val cdnImg: String, @JsonProperty("hash") val hash: String, - @JsonProperty("subs") val subs: List?, + @JsonProperty("subs") val subs: ArrayList? = arrayListOf(), @JsonProperty("length") val length: String, @JsonProperty("id") val id: String, @JsonProperty("title") val title: String, @@ -141,5 +146,14 @@ open class StreamSB : ExtractorApi() { url, headers = headers ).forEach(callback) + + mapped.streamData.subs?.map {sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.label.toString(), + sub.file ?: return@map null, + ) + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index 63634704..287fe4d4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -85,18 +85,12 @@ open class VidSrcExtractor : ExtractorApi() { this.name, this.name, srcm3u8, - this.mainUrl, + "https://vidsrc.stream/", Qualities.Unknown.value, extractorData = pass, isM3u8 = true ) ) - -// M3u8Helper.generateM3u8( -// name, -// srcm3u8, -// absoluteUrl -// ).forEach(callback) } else { loadExtractor(linkfixed, url, subtitleCallback, callback) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt index 9e3585ae..15ff0436 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/XStreamCdn.kt @@ -1,12 +1,23 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName +class Cdnplayer: XStreamCdn() { + override val name: String = "Cdnplayer" + override val mainUrl: String = "https://cdnplayer.online" +} + +class Kotakajair: XStreamCdn() { + override val name: String = "Kotakajair" + override val mainUrl: String = "https://kotakajair.xyz" +} + class FEnet: XStreamCdn() { override val name: String = "FEnet" override val mainUrl: String = "https://fembed.net" @@ -59,44 +70,67 @@ open class XStreamCdn : ExtractorApi() { //val type: String // Mp4 ) + private data class Player( + @JsonProperty("poster_file") val poster_file: String? = null, + ) + private data class ResponseJson( @JsonProperty("success") val success: Boolean, - @JsonProperty("data") val data: List? + @JsonProperty("player") val player: Player? = null, + @JsonProperty("data") val data: List?, + @JsonProperty("captions") val captions: List?, + ) + + private data class Captions( + @JsonProperty("id") val id: String, + @JsonProperty("hash") val hash: String, + @JsonProperty("language") val language: String, + @JsonProperty("extension") val extension: String ) override fun getExtractorUrl(id: String): String { return "$domainUrl/api/source/$id" } - override suspend fun getUrl(url: String, referer: String?): List { + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { val headers = mapOf( "Referer" to url, "User-Agent" to "Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0", ) val id = url.trimEnd('/').split("/").last() val newUrl = "https://${domainUrl}/api/source/${id}" - val extractedLinksList: MutableList = mutableListOf() - with(app.post(newUrl, headers = headers)) { - if (this.code != 200) return listOf() - val text = this.text - if (text.isEmpty()) return listOf() - if (text == """{"success":false,"data":"Video not found or has been removed"}""") return listOf() - AppUtils.parseJson(text)?.let { + app.post(newUrl, headers = headers).let { res -> + val sources = tryParseJson(res.text) + sources?.let { if (it.success && it.data != null) { - it.data.forEach { data -> - extractedLinksList.add( + it.data.map { source -> + callback.invoke( ExtractorLink( name, name = name, - data.file, + source.file, url, - getQualityFromName(data.label), + getQualityFromName(source.label), ) ) } } } + + val userData = sources?.player?.poster_file?.split("/")?.get(2) + sources?.captions?.map { + subtitleCallback.invoke( + SubtitleFile( + it?.language.toString(), + "$mainUrl/asset/userdata/$userData/caption/${it?.hash}/${it?.id}.${it?.extension}" + ) + ) + } } - return extractedLinksList } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 8d6b1b82..ca612385 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -235,6 +235,7 @@ val extractorApis: MutableList = arrayListOf( Vidgomunime(), Sbflix(), Streamsss(), + Sbspeed(), Fastream(), @@ -246,6 +247,8 @@ val extractorApis: MutableList = arrayListOf( LayarKaca(), Rasacintaku(), FEnet(), + Kotakajair(), + Cdnplayer(), // WatchSB(), 'cause StreamSB.kt works Uqload(), Uqload1(), @@ -317,6 +320,7 @@ val extractorApis: MutableList = arrayListOf( Linkbox(), Acefile(), SpeedoStream(), + SpeedoStream1(), Zorofile(), Embedgram(), Mvidoo(), @@ -324,6 +328,7 @@ val extractorApis: MutableList = arrayListOf( Vidmoly(), Vidmolyme(), Voe(), + Moviehab(), Gdriveplayerapi(), Gdriveplayerapp(), @@ -336,6 +341,7 @@ val extractorApis: MutableList = arrayListOf( Gdriveplayerco(), Gdriveplayer(), DatabaseGdrive(), + DatabaseGdrive2(), YoutubeExtractor(), YoutubeShortLinkExtractor(), From 044822040f7da8c777958057a12d1f7ee3a4222c Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:55:59 +0200 Subject: [PATCH 026/273] Translation in Bulgarian language. Co-authored-by: ardoslav --- README.md | 1 + .../ui/settings/SettingsGeneral.kt | 1 + app/src/main/res/values-bg/strings.xml | 547 ++++++++++++++++++ 3 files changed, 549 insertions(+) create mode 100644 app/src/main/res/values-bg/strings.xml diff --git a/README.md b/README.md index e7221440..5e961c61 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ ***The list of supported languages:*** * 🇱🇧 Arabic +* 🇧🇬 Bulgarian * 🇭🇷 Croatian * 🇨🇿 Czech * 🇳🇱 Dutch 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 57074e74..8ea76cda 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 @@ -79,6 +79,7 @@ val appLanguages = arrayListOf( Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"), Triple("", "Czech", "cs"), Triple("", "Croatian", "hr"), + Triple("", "Bulgarian", "bg"), ).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top class SettingsGeneral : PreferenceFragmentCompat() { diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml new file mode 100644 index 00000000..b8f43dd0 --- /dev/null +++ b/app/src/main/res/values-bg/strings.xml @@ -0,0 +1,547 @@ + + + + + %s еп. %d + Актьори: %s + Епизод %d ще бъде пуснат след + %DD %dh %dm + %dh %dm + %dm + + + Плакат + @string/result_poster_img_des + Плакат на епизод + Основен плакат + Следващ произволен + Върни се + Смяна на доставчика + Визуализация на фона + + + Скорост (%.2fx) + Оценка: %.1f + Намерена е нова актуализация!\n%s -> %s + Шаблон + %d мин + + CloudStream + Пусни с CloudStream + Начало + Търсене + Изтегляния + Настройки + + Търсене… + Търсете %s... + + Няма данни + Още опций + Следващ епизод + Жанрове + Сподели + Отвори в браузъра + Пропусни зареждането + Зареждане… + + Гледане + На изчакване + Завършено + Изпуснат + План за гледане + Нито един + Повторно гледане + + Пускане на филм + Възпроизвеждане на живо + Поточно предаване на торент + Източници + субтитри + Повторен опит за свързване... + Върни се + Пусни епизод + + + Изтегли + Изтеглено + Изтегля се + Изтеглянето е на пауза + Изтеглянето започна + Изтеглянето се провали + Изтеглянето е отменено + Изтеглянето е завършено + Поток + + Грешка при зареждане на връзки + Вътрешна памет + + Дублаж + Субтитри + + Изтрий файла + Възпроизвеждане на файл + Възобновете изтеглянето + Пауза на изтеглянето + + Деактивирайте автоматичното докладване на грешки + Повече информация + Скрий + Пусни + Информация + Филтриране на отметки + Отметки + Премахване + Задайте статус на гледане + Приложи + Отказ + Копирай + Затвори + Изчисти + Запазване + + Скорост на възпроизвеждане + + Настройки на субтитрите + Цвят на текста + Цвят на контура + Цвят на фона + Цвят на прозореца + Тип ръб + Височина на субтитрите + Шрифт + Размер на шрифта + + Търсене чрез доставчици + Търсене с помощта на типове + + %d Банан/а даден/и на разработчиците + Не е/са даден/и Банан/и + + Автоматичен избор на език + Изтегляне на езици + Език на субтитрите + Задръжте, за да нулирате по подразбиране + Импортирайте шрифтове, като ги поставите в %s + Продължете да гледате + + Премахване + Повече информация + @string/home_play + + Може да е необходим VPN, за да работи правилно този доставчик + Този доставчик е торент, препоръчва се VPN + + Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта. + + Описание + Няма намерен съдържание + Няма намерено описание + + Покажи logcat 🐈 + + Картина в картина + Продължава възпроизвеждането в миниатюрен плейър върху други приложения + Бутон за преоразмеряване на плейъра + Премахнете черните граници + Субтитри + Настройки на субтитрите на плейъра + Chromecast субтитри + Настройки за субтитри на Chromecast + + Режим Eigengravy (промяна скорост на възпроизвеждане) + Добавя опция за скорост в плейъра + Плъзнете за преместване + Плъзнете наляво или надясно, за да контролирате времето във видеоплейъра + Плъзнете, за да промените настройките + Плъзнете наляво или надясно, за да промените яркостта или силата на звука + + Автоматично пускане на следващ епизод + Започнете следващия епизод, когато текущият приключи + + Докоснете двукратно за превъртане + Докоснете два пъти за пауза + Размер на превъртане + Докоснете два пъти от дясната или лявата страна, за да превъртите напред или назад + Докоснете в средата, за да направите пауза + Използвайте яркостта на системата + Използвайте системна яркост в плейъра на приложението вместо тъмно + наслагване + + Актуализирайте прогреса на гледане + Автоматично синхронизирайте прогреса на текущия си епизод + + Възстановете данните от архив + + Архивиране на данни + Зареден архивен файл + Неуспешно възстановяване на данни от файл %s + Успешно съхранени данни + Липсват разрешения за съхранение, моля, опитайте отново + Грешка при архивирането на %s + + Търсене + Акаунти + Актуализации и архивиране + + Информация + Подробно търсене + Дава ви резултатите от търсенето, разделени по доставчик + Изпраща данни само за сривове + Не изпраща данни + Показване заместващ епизод за аниме + Показване на трейлъри + Покажете плакати от kitsu + Скриване на избраното видео качество в резултатите от търсенето + + Автоматични актуализации на плъгини + Показвай актуализации на приложението + Автоматично търси нови актуализации при стартиране + Актуализация до експериментални версии + Търсете експериментални актуализации вместо само пълни версии + Github + Light novel - приложение от същите разработчици + Приложение за аниме от същите разработчици + Присъединете се към Discord + Дайте банан/и разработчиците + Даден/и банан/и + + Език на приложението + + Този доставчик няма поддръжка за Chromecast + Няма намерени връзки + Връзката е копирана в клипборда + Пусни епизода + Възстановяване на стойността по подразбиране + За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до + разработчиците + + Сезон + %s %d%s + Без сезон + Епизод + Епизоди + %d-%d + %d %s + С + Е + Няма намерени епизоди + + Изтрий файла + Изтрий + Пауза + Продължи + -30 + 30 + Това ще изтрие за постоянно %s\nСигурни ли сте? + %dm\nостава + + + Продължава + Завършен + Статус + Година + Рейтинг + Продължителност + Уебсайт + Синопсис + + На опашката + Без субтитри + По подразбиране + + Безплатно + Използвано + Приложения + + + Филми + Телевизионен сериал + Анимационни филми + Аниме + Торенти + Документални + OVA + Азиатски драми + На живо + Разпоретини + други + + + Филм + Серия + Анимационен филм + @string/аниме + @string/ova + Торент + Документален филм + Азиатска драма + Поток на живо + Разпоретинa + Видео + + Грешка в източника + Дистанционна грешка + Грешка в рендъра + Неочаквана грешка на плеъра + Грешка при изтегляне, проверете разрешенията за съхранение + + Епизод за Chromecast + Chromecast огледало + Пусни в приложението + Пусни в %s + Пусни в браузър + Копирай връзка + Автоматично изтегляне + Изтегляне на огледало + Презареждане на връзки + Изтегляне на субтитри + + покажи качество + Покажи дублаж + Покажи субтитри + Заглавие + Превключване на елементите на потребителския интерфейс на плаката + + Няма намерена актуализация + Проверка за актуализация + + Заключен + Преоразмеряване + Източник + Пропусни + + Не показвай отново + Пропуснете тази актуализация + Актуализация + Предпочитано качество за гледане + Максимален брой знаци за заглавие във видеоплейъра + Разделителна способност на видео плейъра + + Размер на видео буфера + Дължина на видео буфера + Видео кеш на диск + Изчистете кеша за видео и изображения + + Ще предизвика случайни сривове, ако е зададено твърде високо. Не променяйте, ако имате малко количество RAM, като Android TV или стар телефон + Може да причини проблеми на системи с малко място за съхранение, като устройства с Android TV, ако го зададете твърде високо + + DNS през HTTPS + Полезно за заобикаляне на блокирания от ISP доставчик + + Сайт за клониране + Премахване на сайта + Добавяне на клонинг на съществуващ сайт с различен URL адрес + + Път за изтегляне + + URL адрес на сървъра на Nginx + + Показване на дублирани/субирани аниме + + Побиране в екрана + Разтягане + Мащабиране + + Опровержение + 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. + + Общ + Случаен бутон + Показване на произволен бутон на началната страница + Езици на доставчика + Оформление на приложението + Предпочитана медия + Активирайте разпоретините на поддържани доставчици + Кодиране на субтитрите + Доставчици + Оформление + + Автоматично + ТВ оформление + Оформление като телефон + Оформление като емулатор + + Основен цвят + Тема на приложението + Местоположение на заглавието на плаката + Поставете заглавието под плаката + + парола123 + MyCoolUsername + hello@world.com + 127.0.0.1 + MyCoolSite + example.com + Езиков код (en) + + + %s %s + Акаунт + Излизане + Влизане + Превключване на акаунт + Добавяне на акаунт + Създай акаунт + Добавете проследяване + Добавен %s + Синхронизиране + Оценен + %d / 10 + /?? + /%d + Удостоверен %s + Неуспешно удостоверяване на %s + + + Нито един + Нормално + Всичко + Макс + Мин + Контур + Удвоени + Сянка + Повдигнати + Синхронизиране на суб + 1000ms + Забавяне на субтитрите + Използвайте това, ако субтитрите се показват %dms твърде рано + Използвайте това, ако субтитрите се показват %dms твърде късно + Без забавяне на субтитрите + + + + Бързата кафява лисица прескача мързеливото куче + + Препоръчва се + Заредено %s + Зареди от файл + Зареди от Интернет + Изтеглен файл + Главен + Поддържащ + Заден план + + Източник + Случаен + + Очаквайте скоро… + + Cam + Cam + Cam + HQ + HD + TS + TC + BlueRay + WP + DVD + 4K + SD + UHD + HDR + SDR + Web + + Снимка на плакат + Плеър + Резолюция и заглавие + Заглавие + Резолюция + Невалиден идентификатор + Невалидни данни + Невалиден адрес + Грешка + Премахнете затворените надписи от субтитрите + Премахнете рекламирането в субтитрите + Филтриране по предпочитан медиен език + Екстри + Трейлър + Връзка към потока + Обратно към + Следващ + Гледайте видеоклипове на тези езици + Предишен + Пропуснете настройката + Променете външния вид на приложението, за да отговаря на вашето устройство + Докладване за сривове + Какво искате да видите + Край + Разширения + Добавяне на хранилище + Име на хранилище + URL адрес на хранилището + Приставката е заредена + Приставката е изтрита + Неуспешно зареждане %s + 18+ + Започна да изтегля %d %s + Изтеглено %d %s успешно + Всички %s вече са изтеглени + Пакетно изтегляне + Плъгин + Плъгини + Това също ще изтрие всички хранилища за плъгини + Изтриване на хранилище + Изтеглете списъка със сайтове, които искате да използвате + Изтеглено: %d + Деактивирано: %d + Не е изтеглено: %d + Актуализирани %d плъгини + CloudStream няма инсталирани сайтове по подразбиране. Трябва да инсталирате сайтовете от хранилища.\n\nПоради безмозъчно премахване на DMCA от Sky Uk Limited 🤮 не можем да свържем сайтовете на хранилищата в приложението.\n\nПрисъединете се към нашия дискорд за връзки или търсете онлайн. + Вижте хранилищата на общността + Публичен списък + Всички субтитри с главни букви + + Изтегляне на всички добавки от това хранилище? + %s (Disabled) + Потоци + Аудио потоци + Видео потоци + Приложете при рестартиране + + Безопасният режим е активиран + Възникна непоправим срив и ние автоматично деактивирахме всички разширения, така че можете да намерите и премахнете разширението, което причинява проблеми. + Вижте информация за срива + + Оценка: %s + Описание + Версия + Статус + Размер + Автори + Поддържани + език + Първо инсталирайте разширението + + HLS плейлист + + Предпочитан видео плеър + Вътрешен плеър + VLC + MPV + Уеб видео предаване + Браузър + Приложението не е намерено + \ No newline at end of file From 0f625142dacb4553b9af57f7f949eff9eab7b218 Mon Sep 17 00:00:00 2001 From: "J. Fronny" Date: Wed, 26 Oct 2022 19:56:31 +0000 Subject: [PATCH 027/273] Switch to kotlin build scripts (#158) --- .github/workflows/prerelease.yml | 4 +- .github/workflows/pull_request.yml | 4 +- .idea/jarRepositories.xml | 5 + app/build.gradle | 233 --------------------- app/build.gradle.kts | 250 +++++++++++++++++++++++ app/proguard-rules.pro | 2 +- app/src/main/AndroidManifest.xml | 3 +- build.gradle | 27 --- build.gradle.kts | 26 +++ gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 2 - settings.gradle.kts | 3 + 12 files changed, 291 insertions(+), 270 deletions(-) delete mode 100644 app/build.gradle create mode 100644 app/build.gradle.kts delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 37161d6b..a7615ad5 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -43,7 +43,7 @@ jobs: echo "::set-output name=key_pwd::$KEY_PWD" - name: Run Gradle run: | - ./gradlew assemblePrerelease makeJar androidSourcesJar + ./gradlew assemblePrereleaseRelease makeJar androidSourcesJar env: SIGNING_KEY_ALIAS: "key0" SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }} @@ -56,6 +56,6 @@ jobs: prerelease: true title: "Pre-release Build" files: | - app/build/outputs/apk/prerelease/*.apk + app/build/outputs/apk/prerelease/stable/*.apk app/build/libs/app-sources.jar app/build/classes.jar diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1a4db134..36199cd6 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -15,9 +15,9 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Run Gradle - run: ./gradlew assembleDebug + run: ./gradlew assemblePrereleaseDebug - name: Upload Artifact uses: actions/upload-artifact@v2 with: name: pull-request-build - path: "app/build/outputs/apk/debug/*.apk" + path: "app/build/outputs/apk/prerelease/debug/*.apk" diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 652d9f3f..333d4937 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -31,5 +31,10 @@ Общ Случаен бутон Показване на произволен бутон на началната страница From c9c339795ad94dc3027c44c97ab15098d089240c Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:12:17 +0200 Subject: [PATCH 029/273] fix the bulgarian string once again --- app/src/main/res/values-bg/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index d67ab55c..e32d3d93 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -271,7 +271,7 @@ Филм Серия Анимационен филм - @string/аниме + @string/anime @string/ova Торент Документален филм From fa6a620bf995073b48b4d5582e7266dae1a489f2 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:39:36 +0200 Subject: [PATCH 030/273] fix builds --- .github/workflows/prerelease.yml | 2 +- app/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index bac0430f..99ab00df 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -43,7 +43,7 @@ jobs: echo "key_pwd=$KEY_PWD" >> $GITHUB_OUTPUT - name: Run Gradle run: | - ./gradlew assemblePrereleaseRelease makeJar androidSourcesJar + ./gradlew assemblePrerelease makeJar androidSourcesJar env: SIGNING_KEY_ALIAS: "key0" SIGNING_KEY_PASSWORD: ${{ steps.fetch_keystore.outputs.key_pwd }} diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ed2ad5a0..42ca93fd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -226,7 +226,7 @@ tasks.register("androidSourcesJar", Jar::class) { // this is used by the gradlew plugin tasks.register("makeJar", Copy::class) { - from("build/intermediates/compile_app_classes_jar/debug") + from("build/intermediates/compile_app_classes_jar/prereleaseDebug") into("build") include("classes.jar") dependsOn("build") From 7e750a40e055b15ac303cf6eb8b977fa9a72de47 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Wed, 26 Oct 2022 23:09:28 +0200 Subject: [PATCH 031/273] fix da build (again) --- .github/workflows/prerelease.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 99ab00df..4ce7dba1 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -56,6 +56,6 @@ jobs: prerelease: true title: "Pre-release Build" files: | - app/build/outputs/apk/prerelease/stable/*.apk + app/build/outputs/apk/prerelease/release/*.apk app/build/libs/app-sources.jar app/build/classes.jar From 93cbd29f3dce5ee994030051f6a51271d330a631 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Thu, 27 Oct 2022 15:38:53 +0200 Subject: [PATCH 032/273] Bumped the exoplayer version and fixed the audio & video track selection --- app/build.gradle.kts | 8 +- .../cloudstream3/ui/ControllerActivity.kt | 14 +-- .../cloudstream3/ui/player/CS3IPlayer.kt | 100 ++++++++++++++---- .../cloudstream3/ui/player/GeneratorPlayer.kt | 5 +- .../cloudstream3/ui/player/IPlayer.kt | 8 +- .../ui/player/NonFinalTextRenderer.kt | 5 +- .../ui/subtitles/SubtitlesFragment.kt | 2 +- .../cloudstream3/utils/CastOptionsProvider.kt | 9 +- 8 files changed, 108 insertions(+), 43 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 42ca93fd..b3318675 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -155,10 +155,10 @@ dependencies { // implementation("androidx.leanback:leanback-paging:1.1.0-alpha09") // Exoplayer - implementation("com.google.android.exoplayer:exoplayer:2.16.1") - implementation("com.google.android.exoplayer:extension-cast:2.16.1") - implementation("com.google.android.exoplayer:extension-mediasession:2.16.1") - implementation("com.google.android.exoplayer:extension-okhttp:2.16.1") + implementation("com.google.android.exoplayer:exoplayer:2.18.1") + implementation("com.google.android.exoplayer:extension-cast:2.18.1") + implementation("com.google.android.exoplayer:extension-mediasession:2.18.1") + implementation("com.google.android.exoplayer:extension-okhttp:2.18.1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 ad0b4399..46ddce09 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt @@ -150,7 +150,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } else { ChromecastSubtitlesFragment.getCurrentSavedStyle().apply { val font = TextTrackStyle() - font.fontFamily = fontFamily ?: "Google Sans" + font.setFontFamily(fontFamily ?: "Google Sans") fontGenericFamily?.let { font.fontGenericFamily = it } @@ -183,7 +183,9 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl ?: remoteMediaClient?.currentItem?.media?.contentId) - val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }.toTypedArray() + val sortingMethods = + items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } + .toTypedArray() val sotringIndex = items.indexOfFirst { it.url == contentUrl } val arrayAdapter = @@ -279,7 +281,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi val currentPosition = remoteMediaClient?.approximateStreamPosition if (currentDuration != null && currentPosition != null) DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration) - } catch (t : Throwable) { + } catch (t: Throwable) { logError(t) } @@ -358,10 +360,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } } - override fun onSessionConnected(castSession: CastSession?) { - castSession?.let { - super.onSessionConnected(it) - } + override fun onSessionConnected(castSession: CastSession) { + super.onSessionConnected(castSession) remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject()) } } 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 a838c85c..f60d8c78 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 @@ -15,6 +15,7 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.source.* import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.trackselection.DefaultTrackSelector +import com.google.android.exoplayer2.trackselection.TrackSelectionOverride import com.google.android.exoplayer2.trackselection.TrackSelector import com.google.android.exoplayer2.ui.SubtitleView import com.google.android.exoplayer2.upstream.* @@ -218,7 +219,43 @@ class CS3IPlayer : IPlayer { var currentSubtitles: SubtitleData? = null - override fun setMaxVideoSize(width: Int, height: Int) { + private fun List.getTrack(id: String?): Pair? { + if (id == null) return null + // This beast of an expression does: + // 1. Filter all audio tracks + // 2. Get all formats in said audio tacks + // 3. Gets all ids of the formats + // 4. Filters to find the first audio track with the same id as the audio track we are looking for + // 5. Returns the media group and the index of the audio track in the group + return this.firstNotNullOfOrNull { group -> + (0 until group.mediaTrackGroup.length).map { + group.getTrackFormat(it) to it + }.firstOrNull { it.first.id == id } + ?.let { group.mediaTrackGroup to it.second } + } + } + + override fun setMaxVideoSize(width: Int, height: Int, id: String?) { + if (id != null) { + val videoTrack = + exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_VIDEO } + ?.getTrack(id) + + if (videoTrack != null) { + exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters + ?.buildUpon() + ?.setOverrideForType( + TrackSelectionOverride( + videoTrack.first, + videoTrack.second + ) + ) + ?.build() + ?: return + return + } + } + exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters ?.buildUpon() ?.setMaxVideoSize(width, height) @@ -226,8 +263,29 @@ class CS3IPlayer : IPlayer { ?: return } - override fun setPreferredAudioTrack(trackLanguage: String?) { + override fun setPreferredAudioTrack(trackLanguage: String?, id: String?) { preferredAudioTrackLanguage = trackLanguage + + if (id != null) { + val audioTrack = + exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_AUDIO } + ?.getTrack(id) + + if (audioTrack != null) { + exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters + ?.buildUpon() + ?.setOverrideForType( + TrackSelectionOverride( + audioTrack.first, + audioTrack.second + ) + ) + ?.build() + ?: return + return + } + } + exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters ?.buildUpon() ?.setPreferredAudioLanguage(trackLanguage) @@ -239,11 +297,11 @@ class CS3IPlayer : IPlayer { /** * Gets all supported formats in a list * */ - private fun List.getFormats(): List { + private fun List.getFormats(): List> { return this.map { - (0 until it.trackGroup.length).mapNotNull { i -> + (0 until it.mediaTrackGroup.length).mapNotNull { i -> if (it.isSupported) - it.trackGroup.getFormat(i) // to it.isSelected + it.mediaTrackGroup.getFormat(i) to i else null } }.flatten() @@ -270,11 +328,12 @@ class CS3IPlayer : IPlayer { } override fun getVideoTracks(): CurrentTracks { - val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList() - val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats() - .map { it.toVideoTrack() } - val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats() - .map { it.toAudioTrack() } + val allTracks = exoPlayer?.currentTracks?.groups ?: emptyList() + val videoTracks = allTracks.filter { it.type == TRACK_TYPE_VIDEO } + .getFormats() + .map { it.first.toVideoTrack() } + val audioTracks = allTracks.filter { it.type == TRACK_TYPE_AUDIO }.getFormats() + .map { it.first.toAudioTrack() } return CurrentTracks( exoPlayer?.videoFormat?.toVideoTrack(), @@ -611,7 +670,12 @@ class CS3IPlayer : IPlayer { } else it }.toTypedArray() } - .setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight)) + .setTrackSelector( + trackSelector ?: getTrackSelector( + context, + maxVideoHeight + ) + ) .setLoadControl( DefaultLoadControl.Builder() .setTargetBufferBytes( @@ -781,10 +845,7 @@ class CS3IPlayer : IPlayer { isPlaying = exo.isPlaying } exoPlayer?.addListener(object : Player.Listener { - /** - * Records the current used subtitle/track. Needed as exoplayer seems to have loose track language selection. - * */ - override fun onTracksInfoChanged(tracksInfo: TracksInfo) { + override fun onTracksChanged(tracks: Tracks) { fun Format.isSubtitle(): Boolean { return this.sampleMimeType?.contains("video/") == false && this.sampleMimeType?.contains("audio/") == false @@ -792,17 +853,17 @@ class CS3IPlayer : IPlayer { normalSafeApiCall { exoPlayerSelectedTracks = - tracksInfo.trackGroupInfos.mapNotNull { - val format = it.trackGroup.getFormat(0) + tracks.groups.mapNotNull { + val format = it.mediaTrackGroup.getFormat(0) if (format.isSubtitle()) format.language?.let { lang -> lang to it.isSelected } else null } - val exoPlayerReportedTracks = tracksInfo.trackGroupInfos.mapNotNull { + val exoPlayerReportedTracks = tracks.groups.mapNotNull { // Filter out unsupported tracks if (it.isSupported) - it.trackGroup.getFormat(0) + it.mediaTrackGroup.getFormat(0) else null }.mapNotNull { @@ -827,7 +888,6 @@ class CS3IPlayer : IPlayer { onTracksInfoChanged?.invoke() subtitlesUpdates?.invoke() } - super.onTracksInfoChanged(tracksInfo) } override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { 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 e20a6c7b..172984a2 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 @@ -796,15 +796,16 @@ class GeneratorPlayer : FullScreenPlayer() { } tracksDialog.apply_btt?.setOnClickListener { + val currentTrack = currentAudioTracks.getOrNull(audioIndexStart) player.setPreferredAudioTrack( - currentAudioTracks.getOrNull(audioIndexStart)?.language + currentTrack?.language, currentTrack?.id ) val currentVideo = currentVideoTracks.getOrNull(videoIndex) val width = currentVideo?.width ?: NO_VALUE val height = currentVideo?.height ?: NO_VALUE if (width != NO_VALUE && height != NO_VALUE) { - player.setMaxVideoSize(width, height) + player.setMaxVideoSize(width, height, currentVideo?.id) } tracksDialog.dismissSafe(activity) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index e8934250..473b3e65 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -161,9 +161,9 @@ interface IPlayer { fun getVideoTracks(): CurrentTracks - /** If no parameters are set it'll default to no set size */ - fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE) + /** If no parameters are set it'll default to no set size, Specifying the id allows for track overrides to force the player to pick the quality. */ + fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE, id: String? = null) - /** If no trackLanguage is set it'll default to first track */ - fun setPreferredAudioTrack(trackLanguage: String?) + /** If no trackLanguage is set it'll default to first track. Specifying the id allows for track overrides as the language can be identical. */ + fun setPreferredAudioTrack(trackLanguage: String?, id: String? = null) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt index f7fb3139..7acb7c34 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt @@ -75,7 +75,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor( return TAG } - @RendererCapabilities.Capabilities +// @RendererCapabilities.Capabilities override fun supportsFormat(format: Format): Int { return if (decoderFactory.supportsFormat(format)) { RendererCapabilities.create( @@ -202,7 +202,8 @@ open class NonFinalTextRenderer @JvmOverloads constructor( return } // Try and read the next subtitle from the source. - @ReadDataResult val result = +// @ReadDataResult + val result = readSource(formatHold, nextInputBuffer, /* readFlags= */0) if (result == C.RESULT_BUFFER_READ) { if (nextInputBuffer.isEndOfStream) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index b97468e7..05a9416d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -49,7 +49,7 @@ data class SaveCaptionStyle( @JsonProperty("foregroundColor") var foregroundColor: Int, @JsonProperty("backgroundColor") var backgroundColor: Int, @JsonProperty("windowColor") var windowColor: Int, - @CaptionStyleCompat.EdgeType +// @CaptionStyleCompat.EdgeType @JsonProperty("edgeType") var edgeType: Int, @JsonProperty("edgeColor") var edgeColor: Int, @FontRes diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt index 17ec775b..ece9a4c0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt @@ -20,10 +20,13 @@ class CastOptionsProvider : OptionsProvider { MediaIntentReceiver.ACTION_FORWARD, MediaIntentReceiver.ACTION_STOP_CASTING ) + + val name = ControllerActivity::class.qualifiedName!! + val compatButtonAction = intArrayOf(1, 3) val notificationOptions = NotificationOptions.Builder() - .setTargetActivityClassName(ControllerActivity::class.qualifiedName) + .setTargetActivityClassName(name) .setActions(buttonActions, compatButtonAction) .setForward30DrawableResId(R.drawable.go_forward_30) .setRewind30DrawableResId(R.drawable.go_back_30) @@ -32,7 +35,7 @@ class CastOptionsProvider : OptionsProvider { val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) - .setExpandedControllerActivityClassName(ControllerActivity::class.qualifiedName) + .setExpandedControllerActivityClassName(name) .build() return CastOptions.Builder() @@ -44,7 +47,7 @@ class CastOptionsProvider : OptionsProvider { .build() } - override fun getAdditionalSessionProviders(p0: Context?): MutableList { + override fun getAdditionalSessionProviders(p0: Context): MutableList { return Collections.emptyList() } } \ No newline at end of file From 6b586388b9ff68469a3312c61a3c7ec96e904609 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Thu, 27 Oct 2022 18:31:38 +0200 Subject: [PATCH 033/273] Revert "Bumped the exoplayer version and fixed the audio & video track selection" This reverts commit 93cbd29f3dce5ee994030051f6a51271d330a631. --- app/build.gradle.kts | 8 +- .../cloudstream3/ui/ControllerActivity.kt | 14 +-- .../cloudstream3/ui/player/CS3IPlayer.kt | 100 ++++-------------- .../cloudstream3/ui/player/GeneratorPlayer.kt | 5 +- .../cloudstream3/ui/player/IPlayer.kt | 8 +- .../ui/player/NonFinalTextRenderer.kt | 5 +- .../ui/subtitles/SubtitlesFragment.kt | 2 +- .../cloudstream3/utils/CastOptionsProvider.kt | 9 +- 8 files changed, 43 insertions(+), 108 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b3318675..42ca93fd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -155,10 +155,10 @@ dependencies { // implementation("androidx.leanback:leanback-paging:1.1.0-alpha09") // Exoplayer - implementation("com.google.android.exoplayer:exoplayer:2.18.1") - implementation("com.google.android.exoplayer:extension-cast:2.18.1") - implementation("com.google.android.exoplayer:extension-mediasession:2.18.1") - implementation("com.google.android.exoplayer:extension-okhttp:2.18.1") + implementation("com.google.android.exoplayer:exoplayer:2.16.1") + implementation("com.google.android.exoplayer:extension-cast:2.16.1") + implementation("com.google.android.exoplayer:extension-mediasession:2.16.1") + implementation("com.google.android.exoplayer:extension-okhttp:2.16.1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 46ddce09..ad0b4399 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/ControllerActivity.kt @@ -150,7 +150,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } else { ChromecastSubtitlesFragment.getCurrentSavedStyle().apply { val font = TextTrackStyle() - font.setFontFamily(fontFamily ?: "Google Sans") + font.fontFamily = fontFamily ?: "Google Sans" fontGenericFamily?.let { font.fontGenericFamily = it } @@ -183,9 +183,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi val contentUrl = (remoteMediaClient?.currentItem?.media?.contentUrl ?: remoteMediaClient?.currentItem?.media?.contentId) - val sortingMethods = - items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" } - .toTypedArray() + val sortingMethods = items.map { "${it.name} ${Qualities.getStringByInt(it.quality)}" }.toTypedArray() val sotringIndex = items.indexOfFirst { it.url == contentUrl } val arrayAdapter = @@ -281,7 +279,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi val currentPosition = remoteMediaClient?.approximateStreamPosition if (currentDuration != null && currentPosition != null) DataStoreHelper.setViewPos(epData.id, currentPosition, currentDuration) - } catch (t: Throwable) { + } catch (t : Throwable) { logError(t) } @@ -360,8 +358,10 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi } } - override fun onSessionConnected(castSession: CastSession) { - super.onSessionConnected(castSession) + override fun onSessionConnected(castSession: CastSession?) { + castSession?.let { + super.onSessionConnected(it) + } remoteMediaClient?.queueSetRepeatMode(REPEAT_MODE_REPEAT_OFF, JSONObject()) } } 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 f60d8c78..a838c85c 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 @@ -15,7 +15,6 @@ import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource import com.google.android.exoplayer2.source.* import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.trackselection.DefaultTrackSelector -import com.google.android.exoplayer2.trackselection.TrackSelectionOverride import com.google.android.exoplayer2.trackselection.TrackSelector import com.google.android.exoplayer2.ui.SubtitleView import com.google.android.exoplayer2.upstream.* @@ -219,43 +218,7 @@ class CS3IPlayer : IPlayer { var currentSubtitles: SubtitleData? = null - private fun List.getTrack(id: String?): Pair? { - if (id == null) return null - // This beast of an expression does: - // 1. Filter all audio tracks - // 2. Get all formats in said audio tacks - // 3. Gets all ids of the formats - // 4. Filters to find the first audio track with the same id as the audio track we are looking for - // 5. Returns the media group and the index of the audio track in the group - return this.firstNotNullOfOrNull { group -> - (0 until group.mediaTrackGroup.length).map { - group.getTrackFormat(it) to it - }.firstOrNull { it.first.id == id } - ?.let { group.mediaTrackGroup to it.second } - } - } - - override fun setMaxVideoSize(width: Int, height: Int, id: String?) { - if (id != null) { - val videoTrack = - exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_VIDEO } - ?.getTrack(id) - - if (videoTrack != null) { - exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters - ?.buildUpon() - ?.setOverrideForType( - TrackSelectionOverride( - videoTrack.first, - videoTrack.second - ) - ) - ?.build() - ?: return - return - } - } - + override fun setMaxVideoSize(width: Int, height: Int) { exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters ?.buildUpon() ?.setMaxVideoSize(width, height) @@ -263,29 +226,8 @@ class CS3IPlayer : IPlayer { ?: return } - override fun setPreferredAudioTrack(trackLanguage: String?, id: String?) { + override fun setPreferredAudioTrack(trackLanguage: String?) { preferredAudioTrackLanguage = trackLanguage - - if (id != null) { - val audioTrack = - exoPlayer?.currentTracks?.groups?.filter { it.type == TRACK_TYPE_AUDIO } - ?.getTrack(id) - - if (audioTrack != null) { - exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters - ?.buildUpon() - ?.setOverrideForType( - TrackSelectionOverride( - audioTrack.first, - audioTrack.second - ) - ) - ?.build() - ?: return - return - } - } - exoPlayer?.trackSelectionParameters = exoPlayer?.trackSelectionParameters ?.buildUpon() ?.setPreferredAudioLanguage(trackLanguage) @@ -297,11 +239,11 @@ class CS3IPlayer : IPlayer { /** * Gets all supported formats in a list * */ - private fun List.getFormats(): List> { + private fun List.getFormats(): List { return this.map { - (0 until it.mediaTrackGroup.length).mapNotNull { i -> + (0 until it.trackGroup.length).mapNotNull { i -> if (it.isSupported) - it.mediaTrackGroup.getFormat(i) to i + it.trackGroup.getFormat(i) // to it.isSelected else null } }.flatten() @@ -328,12 +270,11 @@ class CS3IPlayer : IPlayer { } override fun getVideoTracks(): CurrentTracks { - val allTracks = exoPlayer?.currentTracks?.groups ?: emptyList() - val videoTracks = allTracks.filter { it.type == TRACK_TYPE_VIDEO } - .getFormats() - .map { it.first.toVideoTrack() } - val audioTracks = allTracks.filter { it.type == TRACK_TYPE_AUDIO }.getFormats() - .map { it.first.toAudioTrack() } + val allTracks = exoPlayer?.currentTracksInfo?.trackGroupInfos ?: emptyList() + val videoTracks = allTracks.filter { it.trackType == TRACK_TYPE_VIDEO }.getFormats() + .map { it.toVideoTrack() } + val audioTracks = allTracks.filter { it.trackType == TRACK_TYPE_AUDIO }.getFormats() + .map { it.toAudioTrack() } return CurrentTracks( exoPlayer?.videoFormat?.toVideoTrack(), @@ -670,12 +611,7 @@ class CS3IPlayer : IPlayer { } else it }.toTypedArray() } - .setTrackSelector( - trackSelector ?: getTrackSelector( - context, - maxVideoHeight - ) - ) + .setTrackSelector(trackSelector ?: getTrackSelector(context, maxVideoHeight)) .setLoadControl( DefaultLoadControl.Builder() .setTargetBufferBytes( @@ -845,7 +781,10 @@ class CS3IPlayer : IPlayer { isPlaying = exo.isPlaying } exoPlayer?.addListener(object : Player.Listener { - override fun onTracksChanged(tracks: Tracks) { + /** + * Records the current used subtitle/track. Needed as exoplayer seems to have loose track language selection. + * */ + override fun onTracksInfoChanged(tracksInfo: TracksInfo) { fun Format.isSubtitle(): Boolean { return this.sampleMimeType?.contains("video/") == false && this.sampleMimeType?.contains("audio/") == false @@ -853,17 +792,17 @@ class CS3IPlayer : IPlayer { normalSafeApiCall { exoPlayerSelectedTracks = - tracks.groups.mapNotNull { - val format = it.mediaTrackGroup.getFormat(0) + tracksInfo.trackGroupInfos.mapNotNull { + val format = it.trackGroup.getFormat(0) if (format.isSubtitle()) format.language?.let { lang -> lang to it.isSelected } else null } - val exoPlayerReportedTracks = tracks.groups.mapNotNull { + val exoPlayerReportedTracks = tracksInfo.trackGroupInfos.mapNotNull { // Filter out unsupported tracks if (it.isSupported) - it.mediaTrackGroup.getFormat(0) + it.trackGroup.getFormat(0) else null }.mapNotNull { @@ -888,6 +827,7 @@ class CS3IPlayer : IPlayer { onTracksInfoChanged?.invoke() subtitlesUpdates?.invoke() } + super.onTracksInfoChanged(tracksInfo) } override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) { 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 172984a2..e20a6c7b 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 @@ -796,16 +796,15 @@ class GeneratorPlayer : FullScreenPlayer() { } tracksDialog.apply_btt?.setOnClickListener { - val currentTrack = currentAudioTracks.getOrNull(audioIndexStart) player.setPreferredAudioTrack( - currentTrack?.language, currentTrack?.id + currentAudioTracks.getOrNull(audioIndexStart)?.language ) val currentVideo = currentVideoTracks.getOrNull(videoIndex) val width = currentVideo?.width ?: NO_VALUE val height = currentVideo?.height ?: NO_VALUE if (width != NO_VALUE && height != NO_VALUE) { - player.setMaxVideoSize(width, height, currentVideo?.id) + player.setMaxVideoSize(width, height) } tracksDialog.dismissSafe(activity) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index 473b3e65..e8934250 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -161,9 +161,9 @@ interface IPlayer { fun getVideoTracks(): CurrentTracks - /** If no parameters are set it'll default to no set size, Specifying the id allows for track overrides to force the player to pick the quality. */ - fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE, id: String? = null) + /** If no parameters are set it'll default to no set size */ + fun setMaxVideoSize(width: Int = Int.MAX_VALUE, height: Int = Int.MAX_VALUE) - /** If no trackLanguage is set it'll default to first track. Specifying the id allows for track overrides as the language can be identical. */ - fun setPreferredAudioTrack(trackLanguage: String?, id: String? = null) + /** If no trackLanguage is set it'll default to first track */ + fun setPreferredAudioTrack(trackLanguage: String?) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt index 7acb7c34..f7fb3139 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.kt @@ -75,7 +75,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor( return TAG } -// @RendererCapabilities.Capabilities + @RendererCapabilities.Capabilities override fun supportsFormat(format: Format): Int { return if (decoderFactory.supportsFormat(format)) { RendererCapabilities.create( @@ -202,8 +202,7 @@ open class NonFinalTextRenderer @JvmOverloads constructor( return } // Try and read the next subtitle from the source. -// @ReadDataResult - val result = + @ReadDataResult val result = readSource(formatHold, nextInputBuffer, /* readFlags= */0) if (result == C.RESULT_BUFFER_READ) { if (nextInputBuffer.isEndOfStream) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt index 05a9416d..b97468e7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/subtitles/SubtitlesFragment.kt @@ -49,7 +49,7 @@ data class SaveCaptionStyle( @JsonProperty("foregroundColor") var foregroundColor: Int, @JsonProperty("backgroundColor") var backgroundColor: Int, @JsonProperty("windowColor") var windowColor: Int, -// @CaptionStyleCompat.EdgeType + @CaptionStyleCompat.EdgeType @JsonProperty("edgeType") var edgeType: Int, @JsonProperty("edgeColor") var edgeColor: Int, @FontRes diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt index ece9a4c0..17ec775b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/CastOptionsProvider.kt @@ -20,13 +20,10 @@ class CastOptionsProvider : OptionsProvider { MediaIntentReceiver.ACTION_FORWARD, MediaIntentReceiver.ACTION_STOP_CASTING ) - - val name = ControllerActivity::class.qualifiedName!! - val compatButtonAction = intArrayOf(1, 3) val notificationOptions = NotificationOptions.Builder() - .setTargetActivityClassName(name) + .setTargetActivityClassName(ControllerActivity::class.qualifiedName) .setActions(buttonActions, compatButtonAction) .setForward30DrawableResId(R.drawable.go_forward_30) .setRewind30DrawableResId(R.drawable.go_back_30) @@ -35,7 +32,7 @@ class CastOptionsProvider : OptionsProvider { val mediaOptions = CastMediaOptions.Builder() .setNotificationOptions(notificationOptions) - .setExpandedControllerActivityClassName(name) + .setExpandedControllerActivityClassName(ControllerActivity::class.qualifiedName) .build() return CastOptions.Builder() @@ -47,7 +44,7 @@ class CastOptionsProvider : OptionsProvider { .build() } - override fun getAdditionalSessionProviders(p0: Context): MutableList { + override fun getAdditionalSessionProviders(p0: Context?): MutableList { return Collections.emptyList() } } \ No newline at end of file From 997420a942bd643f17d0fb00d221af9a1faa2ef4 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Fri, 28 Oct 2022 03:51:27 +0200 Subject: [PATCH 034/273] phone UI changes + cache --- .../cloudstream3/ui/APIRepository.kt | 17 +- .../lagradost/cloudstream3/ui/WatchType.kt | 13 +- .../cloudstream3/ui/home/HomeFragment.kt | 235 ++--- .../cloudstream3/ui/home/HomeViewModel.kt | 39 +- .../ui/result/ResultViewModel2.kt | 47 +- .../cloudstream3/ui/search/SearchFragment.kt | 241 ++---- .../ui/settings/extensions/PluginsFragment.kt | 59 +- app/src/main/res/color/chip_color.xml | 5 + app/src/main/res/layout/activity_main.xml | 118 +-- app/src/main/res/layout/fragment_home.xml | 814 +++++++++--------- app/src/main/res/layout/fragment_plugins.xml | 103 +-- app/src/main/res/layout/fragment_search.xml | 81 +- .../main/res/layout/home_select_mainpage.xml | 156 +--- app/src/main/res/layout/tvtypes_chips.xml | 68 ++ .../main/res/layout/tvtypes_chips_scroll.xml | 10 + app/src/main/res/values/styles.xml | 4 + 16 files changed, 905 insertions(+), 1105 deletions(-) create mode 100644 app/src/main/res/color/chip_color.xml create mode 100644 app/src/main/res/layout/tvtypes_chips.xml create mode 100644 app/src/main/res/layout/tvtypes_chips_scroll.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index 0e5e544b..ce695873 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -26,6 +26,8 @@ class APIRepository(val api: MainAPI) { fun isInvalidData(data: String): Boolean { return data.isEmpty() || data == "[]" || data == "about:blank" } + + private val cacheHash: HashMap, LoadResponse> = hashMapOf() } val hasMainPage = api.hasMainPage @@ -39,7 +41,13 @@ class APIRepository(val api: MainAPI) { suspend fun load(url: String): Resource { return safeApiCall { if (isInvalidData(url)) throw ErrorLoadingException() - api.load(api.fixUrl(url)) ?: throw ErrorLoadingException() + val fixedUrl = api.fixUrl(url) + val key = Pair(api.name,url) + cacheHash[key] ?: api.load(fixedUrl)?.also { + // we cache 20 responses because ppl often go back to the same shit + 20 because I dont want to cause too much memory leak + if (cacheHash.size > 20) cacheHash.remove(cacheHash.keys.random()) + cacheHash[key] = it + } ?: throw ErrorLoadingException() } } @@ -75,7 +83,12 @@ class APIRepository(val api: MainAPI) { api.lastHomepageRequest = unixTimeMS nameIndex?.let { api.mainPage.getOrNull(it) }?.let { data -> - listOf(api.getMainPage(page, MainPageRequest(data.name, data.data, data.horizontalImages))) + listOf( + api.getMainPage( + page, + MainPageRequest(data.name, data.data, data.horizontalImages) + ) + ) } ?: run { if (api.sequentialMainPage) { var first = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt index 417b4408..eb4eb666 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WatchType.kt @@ -5,13 +5,12 @@ import androidx.annotation.StringRes import com.lagradost.cloudstream3.R enum class WatchType(val internalId: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int) { - // FIX ICONS - WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_remove_red_eye_24), - COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_check_24), - ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_pause_24), - DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_close_24), - PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_close_24), - NONE(5, R.string.type_none, R.drawable.ic_baseline_remove_red_eye_24); + WATCHING(0, R.string.type_watching, R.drawable.ic_baseline_bookmark_24), + COMPLETED(1, R.string.type_completed, R.drawable.ic_baseline_bookmark_24), + ONHOLD(2, R.string.type_on_hold, R.drawable.ic_baseline_bookmark_24), + DROPPED(3, R.string.type_dropped, R.drawable.ic_baseline_bookmark_24), + PLANTOWATCH(4, R.string.type_plan_to_watch, R.drawable.ic_baseline_bookmark_24), + NONE(5, R.string.type_none, R.drawable.ic_baseline_add_24); companion object { fun fromInternalId(id: Int?) = values().find { value -> value.internalId == id } ?: NONE 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 0d571b76..2f539a33 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 @@ -14,6 +14,8 @@ import android.view.ViewGroup import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getDrawable import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.widget.NestedScrollView @@ -26,11 +28,14 @@ import androidx.recyclerview.widget.RecyclerView import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.button.MaterialButton +import com.google.android.material.chip.Chip +import com.google.android.material.chip.ChipGroup import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings +import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent @@ -43,6 +48,7 @@ import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.AutofitRecyclerView import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment +import com.lagradost.cloudstream3.ui.result.ResultViewModel2.Companion.updateWatchStatus import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setLinearListLayout import com.lagradost.cloudstream3.ui.search.* @@ -50,6 +56,7 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallba import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.isRecyclerScrollable +import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setMaxViewPoolSize import com.lagradost.cloudstream3.utils.Coroutines.ioSafe @@ -61,11 +68,11 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState import com.lagradost.cloudstream3.utils.Event +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar -import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.setImage @@ -82,14 +89,12 @@ import kotlinx.android.synthetic.main.fragment_home.home_loading import kotlinx.android.synthetic.main.fragment_home.home_loading_error import kotlinx.android.synthetic.main.fragment_home.home_loading_shimmer import kotlinx.android.synthetic.main.fragment_home.home_loading_statusbar -import kotlinx.android.synthetic.main.fragment_home.home_main_poster_recyclerview import kotlinx.android.synthetic.main.fragment_home.home_master_recycler import kotlinx.android.synthetic.main.fragment_home.home_plan_to_watch_btt import kotlinx.android.synthetic.main.fragment_home.home_provider_meta_info import kotlinx.android.synthetic.main.fragment_home.home_provider_name import kotlinx.android.synthetic.main.fragment_home.home_reload_connection_open_in_browser import kotlinx.android.synthetic.main.fragment_home.home_reload_connectionerror -import kotlinx.android.synthetic.main.fragment_home.home_statusbar import kotlinx.android.synthetic.main.fragment_home.home_type_completed_btt import kotlinx.android.synthetic.main.fragment_home.home_type_dropped_btt import kotlinx.android.synthetic.main.fragment_home.home_type_on_hold_btt @@ -100,6 +105,8 @@ import kotlinx.android.synthetic.main.fragment_home.home_watch_parent_item_title import kotlinx.android.synthetic.main.fragment_home.result_error_text import kotlinx.android.synthetic.main.fragment_home_tv.* import kotlinx.android.synthetic.main.home_episodes_expanded.* +import kotlinx.android.synthetic.main.tvtypes_chips.* +import kotlinx.android.synthetic.main.tvtypes_chips.view.* import java.util.* const val HOME_BOOKMARK_VALUE_LIST = "home_bookmarked_last_list" @@ -247,16 +254,16 @@ class HomeFragment : Fragment() { } fun getPairList( - anime: MaterialButton?, - cartoons: MaterialButton?, - tvs: MaterialButton?, - docs: MaterialButton?, - movies: MaterialButton?, - asian: MaterialButton?, - livestream: MaterialButton?, - nsfw: MaterialButton?, - others: MaterialButton?, - ): List>> { + anime: Chip?, + cartoons: Chip?, + tvs: Chip?, + docs: Chip?, + movies: Chip?, + asian: Chip?, + livestream: Chip?, + nsfw: Chip?, + others: Chip?, + ): List>> { // This list should be same order as home screen to aid navigation return listOf( Pair(movies, listOf(TvType.Movie, TvType.Torrent)), @@ -271,6 +278,59 @@ class HomeFragment : Fragment() { ) } + private fun getPairList(header: ChipGroup) = getPairList( + header.home_select_anime, + header.home_select_cartoons, + header.home_select_tv_series, + header.home_select_documentaries, + header.home_select_movies, + header.home_select_asian, + header.home_select_livestreams, + header.home_select_nsfw, + header.home_select_others + ) + + fun validateChips(header: ChipGroup?, validTypes: List) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + val isValid = validTypes.any { types.contains(it) } + button?.isVisible = isValid + } + } + + fun updateChips(header: ChipGroup?, selectedTypes: List) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + button?.isChecked = + button?.isVisible == true && selectedTypes.any { types.contains(it) } + } + } + + fun bindChips( + header: ChipGroup?, + selectedTypes: List, + validTypes: List, + callback: (List) -> Unit + ) { + if (header == null) return + val pairList = getPairList(header) + for ((button, types) in pairList) { + val isValid = validTypes.any { types.contains(it) } + button?.isVisible = isValid + button?.isChecked = isValid && selectedTypes.any { types.contains(it) } + button?.setOnCheckedChangeListener { _, _ -> + val list = ArrayList() + for ((sbutton, vvalidTypes) in pairList) { + if (sbutton?.isChecked == true) + list.addAll(vvalidTypes) + } + callback(list) + } + } + } + fun Context.selectHomepage(selectedApiName: String?, callback: (String) -> Unit) { val validAPIs = filterProviderByPreferredMedia().toMutableList() @@ -296,21 +356,9 @@ class HomeFragment : Fragment() { ?.toMutableList() ?: mutableListOf(TvType.Movie, TvType.TvSeries) - val anime = dialog.findViewById(R.id.home_select_anime) - val cartoons = dialog.findViewById(R.id.home_select_cartoons) - val tvs = dialog.findViewById(R.id.home_select_tv_series) - val docs = dialog.findViewById(R.id.home_select_documentaries) - val movies = dialog.findViewById(R.id.home_select_movies) - val asian = dialog.findViewById(R.id.home_select_asian) - val livestream = dialog.findViewById(R.id.home_select_livestreams) - val nsfw = dialog.findViewById(R.id.home_select_nsfw) - val others = dialog.findViewById(R.id.home_select_others) val cancelBtt = dialog.findViewById(R.id.cancel_btt) val applyBtt = dialog.findViewById(R.id.apply_btt) - val pairList = - getPairList(anime, cartoons, tvs, docs, movies, asian, livestream, nsfw, others) - cancelBtt?.setOnClickListener { dialog.dismissSafe() } @@ -355,52 +403,14 @@ class HomeFragment : Fragment() { arrayAdapter.notifyDataSetChanged() } - /** - * Since fire tv is fucked we need to manually define the focus layout. - * Since visible buttons are only known in runtime this is required. - **/ - var lastButton: MaterialButton? = null - - for ((button, validTypes) in pairList) { - val isValid = - validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } } - button?.isVisible = isValid - if (isValid) { - - // Set focus navigation - button?.let { currentButton -> - lastButton?.nextFocusRightId = currentButton.id - lastButton?.id?.let { currentButton.nextFocusLeftId = it } - lastButton = currentButton - } - - fun buttonContains(): Boolean { - return preSelectedTypes.any { validTypes.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - preSelectedTypes.clear() - preSelectedTypes.addAll(validTypes) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - button.isSelected = true - updateList() - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - button.isSelected = true - preSelectedTypes.addAll(validTypes) - } else { - button.isSelected = false - preSelectedTypes.removeAll(validTypes) - } - updateList() - return@setOnLongClickListener true - } - } + bindChips( + dialog.home_select_group, + preSelectedTypes, + validAPIs.flatMap { it.supportedTypes }.distinct() + ) { list -> + preSelectedTypes.clear() + preSelectedTypes.addAll(list) + updateList() } updateList() } @@ -422,7 +432,6 @@ class HomeFragment : Fragment() { } private fun toggleMainVisibility(visible: Boolean) { - home_main_holder?.isVisible = visible home_main_poster_recyclerview?.isVisible = visible } @@ -531,6 +540,51 @@ class HomeFragment : Fragment() { home_random?.visibility = View.GONE } + observe(homeViewModel.preview) { preview -> + when (preview) { + is Resource.Success -> { + home_preview?.isVisible = true + preview.value.apply { + home_preview_tags?.text = tags?.joinToString(" • ") ?: "" + home_preview_tags?.isGone = tags.isNullOrEmpty() + home_preview_image?.setImage(posterUrl, posterHeaders) + home_preview_title?.text = name + home_preview_play?.setOnClickListener { + activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) + //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) + } + home_preview_info?.setOnClickListener { + activity?.loadResult(url, apiName) + //activity.loadSearchResult(random) + } + // very ugly code, but I dont care + val watchType = DataStoreHelper.getResultWatchState(preview.value.getId()) + home_preview_bookmark?.setText(watchType.stringRes) + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, watchType.iconRes),null,null) + home_preview_bookmark?.setOnClickListener { fab -> + activity?.showBottomDialog( + WatchType.values().map { fab.context.getString(it.stringRes) } + .toList(), + DataStoreHelper.getResultWatchState(preview.value.getId()).ordinal, + fab.context.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + val newValue = WatchType.values()[it] + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds(null,getDrawable(home_preview_bookmark.context, newValue.iconRes),null,null) + home_preview_bookmark?.setText(newValue.stringRes) + + updateWatchStatus(preview.value, newValue) + reloadStored() + } + } + } + } + else -> { + home_preview?.isVisible = false + } + } + } + observe(homeViewModel.apiName) { apiName -> currentApiName = apiName // setKey(USER_SELECTED_HOMEPAGE_API, apiName) @@ -563,17 +617,17 @@ class HomeFragment : Fragment() { HomeChildItemAdapter( mutableListOf(), R.layout.home_result_big_grid, - nextFocusUp = home_main_poster_recyclerview.nextFocusUpId, - nextFocusDown = home_main_poster_recyclerview.nextFocusDownId + nextFocusUp = home_main_poster_recyclerview?.nextFocusUpId, + nextFocusDown = home_main_poster_recyclerview?.nextFocusDownId ) { callback -> homeHandleSearch(callback) } - home_main_poster_recyclerview.setLinearListLayout() + home_main_poster_recyclerview?.setLinearListLayout() observe(homeViewModel.randomItems) { items -> if (items.isNullOrEmpty()) { toggleMainVisibility(false) } else { - val tempAdapter = home_main_poster_recyclerview.adapter as HomeChildItemAdapter? + val tempAdapter = home_main_poster_recyclerview?.adapter as? HomeChildItemAdapter? // no need to reload if it has the same data if (tempAdapter != null && tempAdapter.cardList == items) { toggleMainVisibility(true) @@ -938,7 +992,7 @@ class HomeFragment : Fragment() { } } - context?.fixPaddingStatusbarView(home_statusbar) + //context?.fixPaddingStatusbarView(home_statusbar) context?.fixPaddingStatusbar(home_loading_statusbar) home_master_recycler.adapter = @@ -959,33 +1013,6 @@ class HomeFragment : Fragment() { } } // GridLayoutManager(context, 1).also { it.supportsPredictiveItemAnimations() } - if (!isTvSettings()) { - LinearSnapHelper().attachToRecyclerView(home_main_poster_recyclerview) // snap - val centerLayoutManager = - CenterZoomLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) - centerLayoutManager.setOnSizeListener { index -> - (home_main_poster_recyclerview?.adapter as HomeChildItemAdapter?)?.cardList?.get( - index - )?.let { random -> - home_main_play?.setOnClickListener { - activity.loadSearchResult(random, START_ACTION_RESUME_LATEST) - } - home_main_info?.setOnClickListener { - activity.loadSearchResult(random) - } - - home_main_text?.text = - random.name + if (random is AnimeSearchResponse && !random.dubStatus.isNullOrEmpty()) { - random.dubStatus?.joinToString( - prefix = " • ", - separator = " | " - ) { it.name } - } else "" - } - } - home_main_poster_recyclerview?.layoutManager = centerLayoutManager // scale - } - reloadStored() loadHomePage() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index 30fd45c1..0b928a55 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.HomePageList +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.MainAPI import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.mvvm.* @@ -32,7 +33,6 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.VideoDownloadHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.util.* import kotlin.collections.set @@ -58,7 +58,9 @@ class HomeViewModel : ViewModel() { val bookmarks: LiveData>> = _bookmarks private val _resumeWatching = MutableLiveData>() + private val _preview = MutableLiveData>() val resumeWatching: LiveData> = _resumeWatching + val preview: LiveData> = _preview fun loadResumeWatching() = viewModelScope.launchSafe { val resumeWatching = withContext(Dispatchers.IO) { @@ -207,6 +209,7 @@ class HomeViewModel : ViewModel() { expandAndReturn(name) } + private fun load(api: MainAPI?) = viewModelScope.launchSafe { repo = if (api != null) { APIRepository(api) @@ -219,6 +222,7 @@ class HomeViewModel : ViewModel() { if (repo?.hasMainPage == true) { _page.postValue(Resource.Loading()) + _preview.postValue(Resource.Loading()) when (val data = repo?.getMainPage(1, null)) { is Resource.Success -> { @@ -232,8 +236,38 @@ class HomeViewModel : ViewModel() { ExpandableHomepageList(filteredList, 1, home.hasNext) } } - _page.postValue(Resource.Success(expandable)) val items = data.value.mapNotNull { it?.items }.flatten() + items.randomOrNull()?.list?.randomOrNull()?.url?.let { url -> + // backup request in case first fails + var first = repo?.load(url) + if(first == null ||first is Resource.Failure) { + first = repo?.load(items.random().list.random().url) + } + first?.let { + _preview.postValue(it) + } ?: run { + _preview.postValue( + Resource.Failure( + false, + null, + null, + "No repo found, this should never happen" + ) + ) + } + } ?: run { + _preview.postValue( + Resource.Failure( + false, + null, + null, + "No homepage items" + ) + ) + } + + _page.postValue(Resource.Success(expandable)) + //val home = data.value if (items.isNotEmpty()) { @@ -263,6 +297,7 @@ class HomeViewModel : ViewModel() { } } else { _page.postValue(Resource.Success(emptyMap())) + _preview.postValue(Resource.Failure(false, null, null, "No homepage")) } } 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 906b652d..7c099793 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 @@ -412,6 +412,29 @@ class ResultViewModel2 : ViewModel() { return this?.firstOrNull { it.season == season } } + fun updateWatchStatus(currentResponse : LoadResponse, status: WatchType) { + val currentId = currentResponse.getId() + val resultPage = currentResponse + + DataStoreHelper.setResultWatchState(currentId, status.internalId) + val current = DataStoreHelper.getBookmarkedData(currentId) + val currentTime = System.currentTimeMillis() + DataStoreHelper.setBookmarkedData( + currentId, + DataStoreHelper.BookmarkedData( + currentId, + current?.bookmarkedTime ?: currentTime, + currentTime, + resultPage.name, + resultPage.url, + resultPage.apiName, + resultPage.type, + resultPage.posterUrl, + resultPage.year + ) + ) + } + private fun filterName(name: String?): String? { if (name == null) return null Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { @@ -764,28 +787,10 @@ class ResultViewModel2 : ViewModel() { private val _selectPopup: MutableLiveData> = MutableLiveData(Some.None) val selectPopup: LiveData> get() = _selectPopup - fun updateWatchStatus(status: WatchType) { - val currentId = currentId ?: return - val resultPage = currentResponse ?: return - _watchStatus.postValue(status) - DataStoreHelper.setResultWatchState(currentId, status.internalId) - val current = DataStoreHelper.getBookmarkedData(currentId) - val currentTime = System.currentTimeMillis() - DataStoreHelper.setBookmarkedData( - currentId, - DataStoreHelper.BookmarkedData( - currentId, - current?.bookmarkedTime ?: currentTime, - currentTime, - resultPage.name, - resultPage.url, - resultPage.apiName, - resultPage.type, - resultPage.posterUrl, - resultPage.year - ) - ) + fun updateWatchStatus(status: WatchType) { + updateWatchStatus(currentResponse ?: return,status) + _watchStatus.postValue(status) } private fun startChromecast( 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 5f108cd1..8f588bf7 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 @@ -26,15 +26,19 @@ import com.lagradost.cloudstream3.APIHolder.filterSearchResultByFilmQuality import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiSettings +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.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.home.HomeFragment +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.currentSpan import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.loadHomepageList +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.utils.Coroutines.main @@ -46,6 +50,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import kotlinx.android.synthetic.main.fragment_search.* +import kotlinx.android.synthetic.main.tvtypes_chips.* import java.util.concurrent.locks.ReentrantLock const val SEARCH_PREF_TAGS = "search_pref_tags" @@ -130,65 +135,26 @@ class SearchFragment : Fragment() { // Null if defined as a variable // This needs to be run after view created - private fun getPairList(): List>> { - return HomeFragment.getPairList( - search_select_anime, - search_select_cartoons, - search_select_tv_series, - search_select_documentaries, - search_select_movies, - search_select_asian, - search_select_livestreams, - search_select_nsfw, - search_select_others - ) - } private fun reloadRepos(success: Boolean = false) = main { - val pairList = getPairList() - searchViewModel.reloadRepos() context?.filterProviderByPreferredMedia()?.let { validAPIs -> - for ((button, validTypes) in pairList) { - val isValid = - validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } } - button?.isVisible = isValid - if (isValid) { - fun buttonContains(): Boolean { - return selectedSearchTypes.any { validTypes.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - val last = selectedSearchTypes.toSet() - selectedSearchTypes.clear() - selectedSearchTypes.addAll(validTypes) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) - it?.isSelected = true - if (last != selectedSearchTypes.toSet()) // if you click the same button again the it does nothing - search(main_search?.query?.toString()) - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - it?.isSelected = true - selectedSearchTypes.addAll(validTypes) - } else { - it?.isSelected = false - selectedSearchTypes.removeAll(validTypes) - } - it?.context?.setKey(SEARCH_PREF_TAGS, selectedSearchTypes) - search(main_search?.query?.toString()) - return@setOnLongClickListener true - } + bindChips( + home_select_group, + selectedSearchTypes, + validAPIs.flatMap { api -> api.supportedTypes }.distinct() + ) { list -> + if (selectedSearchTypes.toSet() != list.toSet()) { + setKey(SEARCH_PREF_TAGS, selectedSearchTypes) + selectedSearchTypes.clear() + selectedSearchTypes.addAll(list) + search(main_search?.query?.toString()) } } } } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -239,31 +205,67 @@ class SearchFragment : Fragment() { builder.let { dialog -> val isMultiLang = ctx.getApiProviderLangSettings().size > 1 - val anime = dialog.findViewById(R.id.home_select_anime) - val cartoons = dialog.findViewById(R.id.home_select_cartoons) - val tvs = dialog.findViewById(R.id.home_select_tv_series) - val docs = dialog.findViewById(R.id.home_select_documentaries) - val movies = dialog.findViewById(R.id.home_select_movies) - val asian = dialog.findViewById(R.id.home_select_asian) - val livestream = - dialog.findViewById(R.id.home_select_livestreams) - val nsfw = dialog.findViewById(R.id.home_select_nsfw) - val other = dialog.findViewById(R.id.home_select_others) val cancelBtt = dialog.findViewById(R.id.cancel_btt) val applyBtt = dialog.findViewById(R.id.apply_btt) - val pairList = - HomeFragment.getPairList( - anime, - cartoons, - tvs, - docs, - movies, - asian, - livestream, - nsfw, - other - ) + val listView = dialog.findViewById(R.id.listview1) + val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) + listView?.adapter = arrayAdapter + listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE + + listView?.setOnItemClickListener { _, _, i, _ -> + if (currentValidApis.isNotEmpty()) { + val api = currentValidApis[i].name + if (currentSelectedApis.contains(api)) { + listView.setItemChecked(i, false) + currentSelectedApis -= api + } else { + listView.setItemChecked(i, true) + currentSelectedApis += api + } + } + } + + fun updateList(types: List) { + setKey(SEARCH_PREF_TAGS, types.map {it.name}) + + arrayAdapter.clear() + currentValidApis = validAPIs.filter { api -> + api.supportedTypes.any { + types.contains(it) + } + }.sortedBy { it.name.lowercase() } + + val names = currentValidApis.map { + if (isMultiLang) "${ + SubtitleHelper.getFlagFromIso( + it.lang + )?.plus(" ") ?: "" + }${it.name}" else it.name + } + for ((index, api) in currentValidApis.map { it.name }.withIndex()) { + listView?.setItemChecked(index, currentSelectedApis.contains(api)) + } + + //arrayAdapter.notifyDataSetChanged() + arrayAdapter.addAll(names) + arrayAdapter.notifyDataSetChanged() + } + + val selectedSearchTypes = getKey>(SEARCH_PREF_TAGS) + ?.mapNotNull { listName -> + TvType.values().firstOrNull { it.name == listName } + } + ?.toMutableList() + ?: mutableListOf(TvType.Movie, TvType.TvSeries) + + bindChips( + dialog.home_select_group, + selectedSearchTypes, + TvType.values().toList() + ) { list -> + updateList(list) + } cancelBtt?.setOnClickListener { dialog.dismissSafe() @@ -284,90 +286,7 @@ class SearchFragment : Fragment() { context?.setKey(SEARCH_PREF_PROVIDERS, currentSelectedApis.toList()) selectedApis = currentSelectedApis } - - val selectedSearchTypes = context?.getKey>(SEARCH_PREF_TAGS) - ?.mapNotNull { listName -> - TvType.values().firstOrNull { it.name == listName } - } - ?.toMutableList() - ?: mutableListOf(TvType.Movie, TvType.TvSeries) - - val listView = dialog.findViewById(R.id.listview1) - val arrayAdapter = ArrayAdapter(ctx, R.layout.sort_bottom_single_choice) - listView?.adapter = arrayAdapter - listView?.choiceMode = AbsListView.CHOICE_MODE_MULTIPLE - - listView?.setOnItemClickListener { _, _, i, _ -> - if (currentValidApis.isNotEmpty()) { - val api = currentValidApis[i].name - if (currentSelectedApis.contains(api)) { - listView.setItemChecked(i, false) - currentSelectedApis -= api - } else { - listView.setItemChecked(i, true) - currentSelectedApis += api - } - } - } - - fun updateList() { - arrayAdapter.clear() - currentValidApis = validAPIs.filter { api -> - api.supportedTypes.any { - selectedSearchTypes.contains(it) - } - }.sortedBy { it.name.lowercase() } - - val names = currentValidApis.map { - if (isMultiLang) "${ - SubtitleHelper.getFlagFromIso( - it.lang - )?.plus(" ") ?: "" - }${it.name}" else it.name - } - for ((index, api) in currentValidApis.map { it.name }.withIndex()) { - listView?.setItemChecked(index, currentSelectedApis.contains(api)) - } - - //arrayAdapter.notifyDataSetChanged() - arrayAdapter.addAll(names) - arrayAdapter.notifyDataSetChanged() - } - - for ((button, validTypes) in pairList) { - val isValid = - validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } } - button?.isVisible = isValid - if (isValid) { - fun buttonContains(): Boolean { - return selectedSearchTypes.any { validTypes.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - selectedSearchTypes.clear() - selectedSearchTypes.addAll(validTypes) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - button.isSelected = true - updateList() - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - button.isSelected = true - selectedSearchTypes.addAll(validTypes) - } else { - button.isSelected = false - selectedSearchTypes.removeAll(validTypes) - } - updateList() - return@setOnLongClickListener true - } - } - } - updateList() + updateList(selectedSearchTypes.toList()) } } } @@ -380,14 +299,6 @@ class SearchFragment : Fragment() { ?.toMutableList() ?: mutableListOf(TvType.Movie, TvType.TvSeries) - val pairList = getPairList() - fun updateSelectedList(list: MutableList) { - selectedSearchTypes = list - for ((button, validTypes) in pairList) { - button?.isSelected = selectedSearchTypes.any { validTypes.contains(it) } - } - } - if (isTrueTvSettings()) { search_filter.isFocusable = true search_filter.isFocusableInTouchMode = true @@ -500,7 +411,7 @@ class SearchFragment : Fragment() { SEARCH_HISTORY_OPEN -> { searchViewModel.clearSearch() if (searchItem.type.isNotEmpty()) - updateSelectedList(searchItem.type.toMutableList()) + updateChips(home_select_group, searchItem.type.toMutableList()) main_search?.setQuery(searchItem.searchText, true) } SEARCH_HISTORY_REMOVE -> { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index aa302c5a..bacd26c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -9,8 +9,9 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.observe -import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList +import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.bindChips import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar import com.lagradost.cloudstream3.ui.settings.appLanguages @@ -18,6 +19,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.fragment_plugins.* +import kotlinx.android.synthetic.main.tvtypes_chips.* +import kotlinx.android.synthetic.main.tvtypes_chips_scroll.* const val PLUGINS_BUNDLE_NAME = "name" const val PLUGINS_BUNDLE_URL = "url" @@ -145,56 +148,10 @@ class PluginsFragment : Fragment() { pluginViewModel.updatePluginList(context, url) tv_types_scroll_view?.isVisible = true - // 💀💀💀💀💀💀💀 Recyclerview when - val pairList = getPairList( - home_select_anime, - home_select_cartoons, - home_select_tv_series, - home_select_documentaries, - home_select_movies, - home_select_asian, - home_select_livestreams, - home_select_nsfw, - home_select_others - ) - -// val supportedTypes: Array = -// pluginViewModel.filteredPlugins.value!!.second.flatMap { it -> it.plugin.second.tvTypes ?: listOf("Other") }.distinct().toTypedArray() - - // Copy pasted code - for ((button, validTypes) in pairList) { - val validTypesMapped = validTypes.map { it.name } - val isValid = true - //validTypes.any { it -> supportedTypes.contains(it.name) } - button?.isVisible = isValid - if (isValid) { - fun buttonContains(): Boolean { - return pluginViewModel.tvTypes.any { validTypesMapped.contains(it) } - } - - button?.isSelected = buttonContains() - button?.setOnClickListener { - pluginViewModel.tvTypes.clear() - pluginViewModel.tvTypes.addAll(validTypesMapped) - for ((otherButton, _) in pairList) { - otherButton?.isSelected = false - } - button.isSelected = true - pluginViewModel.updateFilteredPlugins() - } - - button?.setOnLongClickListener { - if (!buttonContains()) { - button.isSelected = true - pluginViewModel.tvTypes.addAll(validTypesMapped) - } else { - button.isSelected = false - pluginViewModel.tvTypes.removeAll(validTypesMapped) - } - pluginViewModel.updateFilteredPlugins() - return@setOnLongClickListener true - } - } + bindChips(home_select_group, emptyList(), TvType.values().toList()) { list -> + pluginViewModel.tvTypes.clear() + pluginViewModel.tvTypes.addAll(list.map { it.name }) + pluginViewModel.updateFilteredPlugins() } } } diff --git a/app/src/main/res/color/chip_color.xml b/app/src/main/res/color/chip_color.xml new file mode 100644 index 00000000..f4190551 --- /dev/null +++ b/app/src/main/res/color/chip_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index f8ca8cbd..4905d454 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,79 +1,83 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/homeRoot" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|keyboard|navigation" + android:paddingTop="0dp"> + android:layout_width="match_parent" + android:layout_height="match_parent"> + app:itemTextColor="@color/item_select_color" + app:labelVisibilityMode="unlabeled" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:menu="@menu/bottom_nav_menu" + app:menuGravity="center"> + + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:menu="@menu/bottom_nav_menu" /> + android:id="@+id/nav_host_fragment" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="0dp" + android:layout_height="0dp" + app:defaultNavHost="true" + app:layout_constraintBottom_toTopOf="@+id/cast_mini_controller_holder" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/nav_rail_view" + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/mobile_navigation" /> + android:id="@+id/cast_mini_controller_holder" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintBottom_toTopOf="@+id/nav_view" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@+id/nav_rail_view" + tools:layout_height="100dp"> + android:id="@+id/cast_mini_controller" + class="com.lagradost.cloudstream3.ui.MyMiniControllerFragment" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + + app:castControlButtons="@array/cast_mini_controller_control_buttons" + app:customCastBackgroundColor="?attr/primaryGrayBackground" + tools:ignore="FragmentTagUsage" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 4abdef0b..9d430e3d 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,87 +1,87 @@ + android:layout_height="match_parent" + tools:context=".ui.home.HomeFragment"> + android:id="@+id/home_loading" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:visibility="gone" + tools:visibility="gone"> + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_gravity="center" + android:visibility="gone" + tools:visibility="gone" /> + android:id="@+id/home_loading_shimmer" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" + android:layout_marginTop="15dp" + android:orientation="vertical" + android:paddingTop="40dp" + app:shimmer_auto_start="true" + app:shimmer_base_alpha="0.2" + app:shimmer_duration="@integer/loading_time" + app:shimmer_highlight_alpha="0.3"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="-164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="148dp" + android:layout_height="234dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_width="125dp" + android:layout_height="200dp" + android:layout_gravity="center" + android:layout_margin="@dimen/loading_margin" + android:background="@color/grayShimmer" + android:translationX="164dp" + app:cardCornerRadius="@dimen/loading_radius" /> + android:layout_marginEnd="@dimen/result_padding" + android:orientation="vertical"> @@ -93,168 +93,170 @@ + android:id="@+id/home_loading_statusbar" + android:layout_width="match_parent" + android:layout_height="70dp"> + android:layout_margin="10dp" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/home_change_provider_img_des" + android:src="@drawable/ic_baseline_keyboard_arrow_down_24" /> + + + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/reload_error" + app:icon="@drawable/ic_baseline_autorenew_24" /> - - + android:layout_gravity="center" + android:layout_margin="5dp" + android:minWidth="200dp" + android:text="@string/result_open_in_browser" + app:icon="@drawable/ic_baseline_public_24" /> + android:id="@+id/result_error_text" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_margin="5dp" + android:gravity="center" + android:textColor="?attr/textColor" /> + android:id="@+id/home_loaded" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?attr/primaryBlackBackground" + android:visibility="gone" + tools:visibility="visible"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:id="@+id/home_statusbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/primaryGrayBackground" /> + android:id="@+id/home_settings_bar" + + android:layout_width="match_parent" + android:layout_height="70dp" + android:background="?attr/primaryGrayBackground" + android:visibility="gone"> + android:paddingTop="10dp" + android:paddingEnd="10dp" + android:paddingBottom="10dp"> + android:id="@+id/home_profile_picture_holder" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:layout_marginEnd="10dp" + app:cardCornerRadius="100dp"> + android:id="@+id/home_profile_picture" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="centerCrop" + tools:ignore="ContentDescription" /> + android:layout_width="match_parent" + android:layout_height="40dp" + android:layout_gravity="center_vertical" + android:background="@drawable/search_background" + android:visibility="visible"> + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:iconifiedByDefault="false" + android:paddingStart="-10dp" + app:iconifiedByDefault="false" + app:queryBackground="@color/transparent" + app:queryHint="@string/search_hint" + app:searchIcon="@drawable/search_icon" + tools:ignore="RtlSymmetry" /> + + + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center_vertical" + android:textColor="?attr/textColor" + android:textSize="20sp" + + tools:text="Hello World" /> - - + android:id="@+id/home_provider_meta_info" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:gravity="center_vertical" + android:textColor="?attr/grayTextColor" + android:textSize="14sp" + tools:text="Hello World" /> @@ -278,273 +280,309 @@ --> - + + + android:layout_height="match_parent" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> - - - - - - - - - - - - - + android:layout_height="300dp" + android:layout_gravity="bottom" + android:background="@drawable/background_shadow" /> + + + + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:iconifiedByDefault="false" + android:paddingStart="-10dp" + app:iconifiedByDefault="false" + + app:queryBackground="@color/transparent" + app:queryHint="@string/search_hint" + + app:searchIcon="@drawable/search_icon" + tools:ignore="RtlSymmetry" /> + + + + + + + + + + + + + + + + + + + + + android:id="@+id/home_watch_parent_item_title" + style="@style/WatchHeaderText" + android:layout_gravity="center_vertical" + android:text="@string/continue_watching" /> + android:layout_width="30dp" + android:layout_height="match_parent" + android:layout_gravity="end|center_vertical" + android:layout_marginEnd="5dp" + android:contentDescription="@string/home_more_info" + android:src="@drawable/ic_baseline_arrow_forward_24" + app:tint="?attr/textColor" /> + android:id="@+id/home_watch_child_recyclerview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipToPadding="false" + android:descendantFocusability="afterDescendants" + android:orientation="horizontal" + android:paddingHorizontal="5dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/home_result_grid" /> + android:id="@+id/home_bookmarked_holder" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:visibility="gone" + tools:visibility="visible"> + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusUp="@id/home_watch_child_recyclerview" + android:nextFocusForward="@id/home_bookmarked_child_recyclerview" + android:paddingStart="12dp" + android:paddingTop="5dp" + android:paddingEnd="12dp" + android:paddingBottom="5dp"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="50dp" + android:fadingEdge="horizontal" + android:requiresFadingEdge="horizontal"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:nextFocusLeft="@id/nav_rail_view" + android:nextFocusRight="@id/home_plan_to_watch_btt" + android:text="@string/type_watching" /> + android:nextFocusLeft="@id/home_type_watching_btt" + android:nextFocusRight="@id/home_type_on_hold_btt" + android:text="@string/type_plan_to_watch" /> + android:nextFocusLeft="@id/home_plan_to_watch_btt" + android:nextFocusRight="@id/home_type_dropped_btt" + android:text="@string/type_on_hold" /> + android:nextFocusLeft="@id/home_type_on_hold_btt" + android:nextFocusRight="@id/home_type_completed_btt" + android:text="@string/type_dropped" /> + style="@style/RoundedSelectableButton" + android:nextFocusLeft="@id/home_type_dropped_btt" + android:text="@string/type_completed" /> + android:layout_width="30dp" + android:layout_height="match_parent" + android:layout_gravity="end|center_vertical" + android:layout_marginEnd="5dp" + android:contentDescription="@string/home_more_info" + android:src="@drawable/ic_baseline_arrow_forward_24" + app:tint="?attr/textColor" /> + android:clipToPadding="false" + android:descendantFocusability="afterDescendants" + android:orientation="horizontal" + android:paddingHorizontal="5dp" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/home_result_grid" /> + android:id="@+id/home_master_recycler" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:descendantFocusability="afterDescendants" + android:nextFocusLeft="@id/nav_rail_view" + tools:listitem="@layout/homepage_parent" /> + android:id="@+id/home_api_fab" + style="@style/ExtendedFloatingActionButton" + android:text="@string/home_source" + android:textColor="?attr/textColor" + android:visibility="gone" + app:icon="@drawable/ic_baseline_filter_list_24" + tools:ignore="ContentDescription" + tools:visibility="visible" /> + android:id="@+id/home_random" + style="@style/ExtendedFloatingActionButton" + android:layout_gravity="bottom|start" + android:text="@string/home_random" + android:textColor="?attr/textColor" + android:visibility="gone" + app:icon="@drawable/ic_baseline_play_arrow_24" + tools:ignore="ContentDescription" + tools:visibility="visible" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_plugins.xml b/app/src/main/res/layout/fragment_plugins.xml index 15e0d2f9..40a0299c 100644 --- a/app/src/main/res/layout/fragment_plugins.xml +++ b/app/src/main/res/layout/fragment_plugins.xml @@ -25,108 +25,7 @@ app:titleTextColor="?attr/textColor" tools:title="Overlord" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_width="match_parent" + android:layout_height="60dp"> + + - + - + + - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/tvtypes_chips.xml b/app/src/main/res/layout/tvtypes_chips.xml new file mode 100644 index 00000000..33ca7762 --- /dev/null +++ b/app/src/main/res/layout/tvtypes_chips.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/tvtypes_chips_scroll.xml b/app/src/main/res/layout/tvtypes_chips_scroll.xml new file mode 100644 index 00000000..45b27dbc --- /dev/null +++ b/app/src/main/res/layout/tvtypes_chips_scroll.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9840cb80..9e235bf3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -75,6 +75,10 @@ @color/amoledModeLight + + @@ -145,7 +151,7 @@ @color/colorPrimaryBlue #4855A2 #5A6BCB - #5A6BCB + @color/whiteText @color/colorPrimaryBlue @@ -155,7 +161,7 @@ @color/colorPrimaryPurple #4704A3 #7125DB - #7125DB + @color/whiteText @color/colorPrimaryPurple @@ -164,7 +170,7 @@ @color/colorPrimaryGreen #007363 #39C1AE - #39C1AE + @color/blackText @color/colorPrimaryGreen @@ -173,7 +179,7 @@ @color/colorPrimaryGreenApple #319B5A #51C57E - #51C57E + @color/blackText @color/colorPrimaryGreenApple @@ -182,7 +188,7 @@ @color/colorPrimaryRed #B62B2B @color/colorPrimaryRed - @color/colorPrimaryRed + @color/whiteText @color/colorPrimaryRed @@ -192,7 +198,7 @@ @color/colorPrimaryBanana #9B7D31 #C5B251 - #C5A851 + @color/blackText @color/colorPrimaryBanana @@ -201,7 +207,7 @@ @color/colorPrimaryParty #C1495B #FD798C - #BF5968 + @color/blackText @color/colorPrimaryParty @@ -210,7 +216,7 @@ @color/colorPrimaryPink #DD1280 #FF4DAE - #DD1280 + @color/blackText @color/colorPrimaryPink @@ -219,7 +225,7 @@ @color/colorPrimaryCarnationPink #83366f #BD5DA5 - #BD5DA5 + @color/blackText @color/colorPrimaryCarnationPink @@ -229,7 +235,7 @@ @color/colorPrimaryMaroon #370C0C #451010 - #451010 + @color/whiteText @color/colorPrimaryMaroon @@ -239,7 +245,7 @@ @color/colorPrimaryDarkGreen #003d00 #004500 - #004500 + @color/whiteText @color/colorPrimaryDarkGreen @@ -249,7 +255,7 @@ @color/colorPrimaryNavyBlue #000073 #000080 - #000080 + @color/whiteText @color/colorPrimaryNavyBlue @@ -259,7 +265,7 @@ @color/colorPrimaryGrey #484848 #515151 - #515151 + @color/whiteText @color/colorPrimaryGrey @@ -269,7 +275,7 @@ @color/colorPrimaryWhite #CCCCCC #FFFFFF - #FFFFFF + @color/blackText @color/colorPrimaryWhite @@ -279,7 +285,7 @@ @color/colorPrimaryBrown #582700 #622C00 - #622C00 + @color/whiteText @color/colorPrimaryBrown From d349190238d99e0604e71c4be0dcff42fbfb08f4 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Mon, 31 Oct 2022 01:16:15 +0100 Subject: [PATCH 041/273] scroll --- .../cloudstream3/ExampleInstrumentedTest.kt | 4 +- .../com/lagradost/cloudstream3/MainAPI.kt | 2 +- .../lagradost/cloudstream3/MainActivity.kt | 2 +- .../lagradost/cloudstream3/ParCollections.kt | 24 ++- .../cloudstream3/extractors/Fastream.kt | 6 +- .../cloudstream3/extractors/Pelisplus.kt | 10 +- .../extractors/VidSrcExtractor.kt | 6 +- .../cloudstream3/extractors/Vidstream.kt | 10 +- .../cloudstream3/extractors/Zplayer.kt | 4 +- .../extractors/helper/AsianEmbedHelper.kt | 4 +- .../metaproviders/CrossTmdbProvider.kt | 6 +- .../metaproviders/MultiAnimeProvider.kt | 2 +- .../cloudstream3/plugins/PluginManager.kt | 21 ++- .../cloudstream3/plugins/RepositoryManager.kt | 4 +- .../cloudstream3/ui/APIRepository.kt | 23 ++- .../cloudstream3/ui/home/HomeFragment.kt | 103 +++++++------ .../cloudstream3/ui/home/HomeScrollAdapter.kt | 60 ++++++++ .../ui/home/HomeScrollTransformer.kt | 13 ++ .../cloudstream3/ui/home/HomeViewModel.kt | 39 ++++- .../cloudstream3/ui/player/GeneratorPlayer.kt | 2 +- .../cloudstream3/ui/player/LinkGenerator.kt | 4 +- .../ui/result/ResultViewModel2.kt | 2 +- .../cloudstream3/ui/result/SyncViewModel.kt | 4 +- .../cloudstream3/ui/search/SearchViewModel.kt | 6 +- .../extensions/ExtensionsViewModel.kt | 4 +- .../settings/extensions/PluginsViewModel.kt | 4 +- .../cloudstream3/utils/BackupUtils.kt | 2 - app/src/main/res/layout/fragment_home.xml | 141 ++++++++++-------- .../lagradost/cloudstream3/ProviderTests.kt | 4 +- 29 files changed, 337 insertions(+), 179 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollTransformer.kt diff --git a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt index 201ddea3..2a3e13e5 100644 --- a/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/lagradost/cloudstream3/ExampleInstrumentedTest.kt @@ -180,7 +180,7 @@ class ExampleInstrumentedTest { @Test fun providerCorrectHomepage() { runBlocking { - getAllProviders().apmap { api -> + getAllProviders().amap { api -> if (api.hasMainPage) { try { val homepage = api.getMainPage() @@ -217,7 +217,7 @@ class ExampleInstrumentedTest { runBlocking { val invalidProvider = ArrayList>() val providers = getAllProviders() - providers.apmap { api -> + providers.amap { api -> try { println("Trying $api") if (testSingleProviderApi(api)) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 940d3391..da257d69 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -1066,7 +1066,7 @@ interface LoadResponse { ) { if (!isTrailersEnabled || trailerUrls == null) return trailers.addAll(trailerUrls.map { TrailerData(it, referer, addRaw) }) - /*val trailers = trailerUrls.filter { it.isNotBlank() }.apmap { trailerUrl -> + /*val trailers = trailerUrls.filter { it.isNotBlank() }.amap { trailerUrl -> val links = arrayListOf() val subs = arrayListOf() if (!loadExtractor( diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 4c193f42..eb83b08a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -592,7 +592,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { api.init() } - inAppAuths.apmap { api -> + inAppAuths.amap { api -> try { api.initialize() } catch (e: Exception) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt index badb6631..46955427 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt @@ -1,8 +1,7 @@ package com.lagradost.cloudstream3 import com.lagradost.cloudstream3.mvvm.logError -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.* //https://stackoverflow.com/questions/34697828/parallel-operations-on-kotlin-collections /* @@ -26,10 +25,25 @@ fun Iterable.pmap( return ArrayList(destination) }*/ + +@OptIn(DelicateCoroutinesApi::class) +suspend fun Map.amap(f: suspend (Map.Entry) -> R): List = + with(CoroutineScope(GlobalScope.coroutineContext)) { + map { async { f(it) } }.map { it.await() } + } + fun Map.apmap(f: suspend (Map.Entry) -> R): List = runBlocking { map { async { f(it) } }.map { it.await() } } + +@OptIn(DelicateCoroutinesApi::class) +suspend fun List.amap(f: suspend (A) -> B): List = + with(CoroutineScope(GlobalScope.coroutineContext)) { + map { async { f(it) } }.map { it.await() } + } + + fun List.apmap(f: suspend (A) -> B): List = runBlocking { map { async { f(it) } }.map { it.await() } } @@ -38,6 +52,12 @@ fun List.apmapIndexed(f: suspend (index: Int, A) -> B): List = runB mapIndexed { index, a -> async { f(index, a) } }.map { it.await() } } +@OptIn(DelicateCoroutinesApi::class) +suspend fun List.amapIndexed(f: suspend (index: Int, A) -> B): List = + with(CoroutineScope(GlobalScope.coroutineContext)) { + mapIndexed { index, a -> async { f(index, a) } }.map { it.await() } + } + // run code in parallel /*fun argpmap( vararg transforms: () -> R, diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt index 16b109be..0d5a5c78 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt @@ -1,6 +1,6 @@ package com.lagradost.cloudstream3.extractors -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink @@ -21,10 +21,10 @@ class Fastream: ExtractorApi() { Pair("file_code",id), Pair("auto","1") )).document - response.select("script").apmap { script -> + response.select("script").amap { script -> if (script.data().contains("sources")) { val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)") - val m3u8 = m3u8regex.find(script.data())?.value ?: return@apmap + val m3u8 = m3u8regex.find(script.data())?.value ?: return@amap generateM3u8( name, m3u8, diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt index cc743d5e..de469b22 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt @@ -1,7 +1,7 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.utils.ExtractorLink @@ -35,7 +35,7 @@ class Pelisplus(val mainUrl: String) { callback: (ExtractorLink) -> Unit ): Boolean { try { - normalApis.apmap { api -> + normalApis.amap { api -> val url = api.getExtractorUrl(id) api.getSafeUrl(url, subtitleCallback = subtitleCallback, callback = callback) } @@ -51,8 +51,8 @@ class Pelisplus(val mainUrl: String) { val qualityRegex = Regex("(\\d+)P") //a[download] - pageDoc.select(".dowload > a")?.apmap { element -> - val href = element.attr("href") ?: return@apmap + pageDoc.select(".dowload > a")?.amap { element -> + val href = element.attr("href") ?: return@amap val qual = if (element.text() .contains("HDP") ) "1080" else qualityRegex.find(element.text())?.destructured?.component1() @@ -84,7 +84,7 @@ class Pelisplus(val mainUrl: String) { //val name = element.text() // Matches vidstream links with extractors - extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api -> + extractorApis.filter { !it.requiresReferer || !isCasting }.amap { api -> if (link.startsWith(api.mainUrl)) { api.getSafeUrl(link, extractorUrl, subtitleCallback, callback) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index 287fe4d4..b910f9dd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -1,7 +1,7 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import kotlinx.coroutines.delay @@ -69,12 +69,12 @@ open class VidSrcExtractor : ExtractorApi() { } else "" } - serverslist.apmap { server -> + serverslist.amap { server -> val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/") if (linkfixed.contains("/pro")) { val srcresponse = app.get(server, referer = absoluteUrl).text val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)") - val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@apmap + val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@amap val passRegex = Regex("""['"](.*set_pass[^"']*)""") val pass = passRegex.find(srcresponse)?.groupValues?.get(1)?.replace( Regex("""^//"""), "https://" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt index 1d853b2d..7eb7fbac 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidstream.kt @@ -1,7 +1,7 @@ package com.lagradost.cloudstream3.extractors import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.argamap import com.lagradost.cloudstream3.utils.ExtractorLink @@ -37,7 +37,7 @@ class Vidstream(val mainUrl: String) { val extractorUrl = getExtractorUrl(id) argamap( { - normalApis.apmap { api -> + normalApis.amap { api -> val url = api.getExtractorUrl(id) api.getSafeUrl( url, @@ -55,8 +55,8 @@ class Vidstream(val mainUrl: String) { val qualityRegex = Regex("(\\d+)P") //a[download] - pageDoc.select(".dowload > a")?.apmap { element -> - val href = element.attr("href") ?: return@apmap + pageDoc.select(".dowload > a")?.amap { element -> + val href = element.attr("href") ?: return@amap val qual = if (element.text() .contains("HDP") ) "1080" else qualityRegex.find(element.text())?.destructured?.component1() @@ -87,7 +87,7 @@ class Vidstream(val mainUrl: String) { //val name = element.text() // Matches vidstream links with extractors - extractorApis.filter { !it.requiresReferer || !isCasting }.apmap { api -> + extractorApis.filter { !it.requiresReferer || !isCasting }.amap { api -> if (link.startsWith(api.mainUrl)) { api.getSafeUrl(link, extractorUrl, subtitleCallback, callback) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt index 6108d2c5..d17b427d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zplayer.kt @@ -1,6 +1,6 @@ package com.lagradost.cloudstream3.extractors -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink @@ -36,7 +36,7 @@ open class ZplayerV2 : ExtractorApi() { val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)") m3u8regex.findAll(testdata).map { it.value - }.toList().apmap { urlm3u8 -> + }.toList().amap { urlm3u8 -> if (urlm3u8.contains("m3u8")) { val testurl = app.get(urlm3u8, headers = mapOf("Referer" to url)).text if (testurl.contains("EXTM3U")) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/helper/AsianEmbedHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/helper/AsianEmbedHelper.kt index e70a9474..0b401c06 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/helper/AsianEmbedHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/helper/AsianEmbedHelper.kt @@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.extractors.helper import android.util.Log import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor @@ -18,7 +18,7 @@ class AsianEmbedHelper { val doc = app.get(url).document val links = doc.select("div#list-server-more > ul > li.linkserver") if (!links.isNullOrEmpty()) { - links.apmap { + links.amap { val datavid = it.attr("data-video") ?: "" //Log.i("AsianEmbed", "Result => (datavid) ${datavid}") if (datavid.isNotBlank()) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt index b01d188c..07aa904e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/CrossTmdbProvider.kt @@ -39,7 +39,7 @@ class CrossTmdbProvider : TmdbProvider() { ): Boolean { tryParseJson(data)?.let { metaData -> if (!metaData.isSuccess) return false - metaData.movies?.apmap { (apiName, data) -> + metaData.movies?.amap { (apiName, data) -> getApiFromNameNull(apiName)?.let { try { it.loadLinks(data, isCasting, subtitleCallback, callback) @@ -64,10 +64,10 @@ class CrossTmdbProvider : TmdbProvider() { val matchName = filterName(this.name) when (this) { is MovieLoadResponse -> { - val data = validApis.apmap { api -> + val data = validApis.amap { api -> try { if (api.supportedTypes.contains(TvType.Movie)) { //|| api.supportedTypes.contains(TvType.AnimeMovie) - return@apmap api.search(this.name)?.first { + return@amap api.search(this.name)?.first { if (filterName(it.name).equals( matchName, ignoreCase = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt index 0ab44b68..e8ac1876 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/MultiAnimeProvider.kt @@ -45,7 +45,7 @@ class MultiAnimeProvider : MainAPI() { override suspend fun load(url: String): LoadResponse? { return syncApi.getResult(url)?.let { res -> - val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).apmap { url -> + val data = SyncUtil.getUrlsFromId(res.id, syncUtilType).amap { url -> validApis.firstOrNull { api -> url.startsWith(api.mainUrl) }?.load(url) }.filterNotNull() 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 35e041be..c0f35601 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -217,18 +217,17 @@ object PluginManager { * 3. If outdated download and load the plugin * 4. Else load the plugin normally **/ - fun updateAllOnlinePluginsAndLoadThem(activity: Activity) { + fun updateAllOnlinePluginsAndLoadThem(activity: Activity) = ioSafe { // Load all plugins as fast as possible! loadAllOnlinePlugins(activity) - ioSafe { afterPluginsLoadedEvent.invoke(true) - } + 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 } @@ -249,7 +248,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) @@ -270,9 +269,9 @@ object PluginManager { createNotification(activity, updatedPlugins) } - ioSafe { + // ioSafe { afterPluginsLoadedEvent.invoke(true) - } + // } Log.i(TAG, "Plugin update done!") } @@ -280,9 +279,9 @@ object PluginManager { /** * Use updateAllOnlinePluginsAndLoadThem * */ - fun loadAllOnlinePlugins(activity: Activity) { + fun loadAllOnlinePlugins(activity: Activity) = ioSafe { // Load all plugins as fast as possible! - (getPluginsOnline()).toList().apmap { pluginData -> + (getPluginsOnline()).toList().amap { pluginData -> loadPlugin( activity, File(pluginData.filePath), @@ -291,7 +290,7 @@ object PluginManager { } } - fun loadAllLocalPlugins(activity: Activity) { + fun loadAllLocalPlugins(activity: Activity) = ioSafe { val dir = File(LOCAL_PLUGINS_PATH) removeKey(PLUGINS_KEY_LOCAL) @@ -299,7 +298,7 @@ object PluginManager { val res = dir.mkdirs() if (!res) { Log.w(TAG, "Failed to create local directories") - return + return@ioSafe } } 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 e3203787..2564abd0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt @@ -4,7 +4,7 @@ import android.content.Context import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall @@ -95,7 +95,7 @@ object RepositoryManager { * */ suspend fun getRepoPlugins(repositoryUrl: String): List>? { val repo = parseRepository(repositoryUrl) ?: return null - return repo.pluginLists.apmap { url -> + return repo.pluginLists.amap { url -> parsePlugins(url).map { repositoryUrl to it } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt index ce695873..ef50019c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/APIRepository.kt @@ -6,6 +6,10 @@ import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.safeApiCall import com.lagradost.cloudstream3.utils.ExtractorLink +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.GlobalScope.coroutineContext +import kotlinx.coroutines.async import kotlinx.coroutines.delay class APIRepository(val api: MainAPI) { @@ -27,7 +31,7 @@ class APIRepository(val api: MainAPI) { return data.isEmpty() || data == "[]" || data == "about:blank" } - private val cacheHash: HashMap, LoadResponse> = hashMapOf() + private val cacheHash: HashMap, LoadResponse> = hashMapOf() } val hasMainPage = api.hasMainPage @@ -42,7 +46,7 @@ class APIRepository(val api: MainAPI) { return safeApiCall { if (isInvalidData(url)) throw ErrorLoadingException() val fixedUrl = api.fixUrl(url) - val key = Pair(api.name,url) + val key = Pair(api.name, url) cacheHash[key] ?: api.load(fixedUrl)?.also { // we cache 20 responses because ppl often go back to the same shit + 20 because I dont want to cause too much memory leak if (cacheHash.size > 20) cacheHash.remove(cacheHash.keys.random()) @@ -78,6 +82,7 @@ class APIRepository(val api: MainAPI) { delay(delta) } + @OptIn(DelicateCoroutinesApi::class) suspend fun getMainPage(page: Int, nameIndex: Int? = null): Resource> { return safeApiCall { api.lastHomepageRequest = unixTimeMS @@ -103,11 +108,15 @@ class APIRepository(val api: MainAPI) { ) } } else { - api.mainPage.apmap { data -> - api.getMainPage( - page, - MainPageRequest(data.name, data.data, data.horizontalImages) - ) + with(CoroutineScope(coroutineContext)) { + api.mainPage.map { data -> + async { + api.getMainPage( + page, + MainPageRequest(data.name, data.data, data.horizontalImages) + ) + } + }.map { it.await() } } } } 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 e86a0742..9792fa43 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 @@ -547,51 +547,14 @@ class HomeFragment : Fragment() { when (preview) { is Resource.Success -> { home_preview?.isVisible = true - - preview.value.apply { - home_preview_tags?.text = tags?.joinToString(" • ") ?: "" - home_preview_tags?.isGone = tags.isNullOrEmpty() - home_preview_image?.setImage(posterUrl, posterHeaders) - home_preview_title?.text = name - home_preview_play?.setOnClickListener { - activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) - //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) - } - home_preview_info?.setOnClickListener { - activity?.loadResult(url, apiName) - //activity.loadSearchResult(random) - } - // very ugly code, but I dont care - val watchType = DataStoreHelper.getResultWatchState(preview.value.getId()) - home_preview_bookmark?.setText(watchType.stringRes) - home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( - null, - getDrawable(home_preview_bookmark.context, watchType.iconRes), - null, - null - ) - home_preview_bookmark?.setOnClickListener { fab -> - activity?.showBottomDialog( - WatchType.values().map { fab.context.getString(it.stringRes) } - .toList(), - DataStoreHelper.getResultWatchState(preview.value.getId()).ordinal, - fab.context.getString(R.string.action_add_to_bookmarks), - showApply = false, - {}) { - val newValue = WatchType.values()[it] - home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( - null, - getDrawable(home_preview_bookmark.context, newValue.iconRes), - null, - null - ) - home_preview_bookmark?.setText(newValue.stringRes) - - updateWatchStatus(preview.value, newValue) - reloadStored() - } - } + (home_preview_viewpager?.adapter as? HomeScrollAdapter)?.apply { + setItems(preview.value) + // home_preview_viewpager?.setCurrentItem(1000, false) } + + //.also { + //home_preview_viewpager?.adapter = + //} } else -> { home_preview?.isVisible = false @@ -606,6 +569,58 @@ class HomeFragment : Fragment() { searchText.setTextColor(color) searchText.setHintTextColor(color) } + + home_preview_viewpager?.apply { + setPageTransformer(false, HomeScrollTransformer()) + adapter = HomeScrollAdapter { load -> + load.apply { + home_preview_tags?.text = tags?.joinToString(" • ") ?: "" + home_preview_tags?.isGone = tags.isNullOrEmpty() + home_preview_image?.setImage(posterUrl, posterHeaders) + home_preview_title?.text = name + home_preview_play?.setOnClickListener { + activity?.loadResult(url, apiName, START_ACTION_RESUME_LATEST) + //activity.loadSearchResult(url, START_ACTION_RESUME_LATEST) + } + home_preview_info?.setOnClickListener { + activity?.loadResult(url, apiName) + //activity.loadSearchResult(random) + } + // very ugly code, but I dont care + val watchType = DataStoreHelper.getResultWatchState(load.getId()) + home_preview_bookmark?.setText(watchType.stringRes) + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( + null, + getDrawable(home_preview_bookmark.context, watchType.iconRes), + null, + null + ) + home_preview_bookmark?.setOnClickListener { fab -> + activity?.showBottomDialog( + WatchType.values().map { fab.context.getString(it.stringRes) } + .toList(), + DataStoreHelper.getResultWatchState(load.getId()).ordinal, + fab.context.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + val newValue = WatchType.values()[it] + home_preview_bookmark?.setCompoundDrawablesWithIntrinsicBounds( + null, + getDrawable(home_preview_bookmark.context, newValue.iconRes), + null, + null + ) + home_preview_bookmark?.setText(newValue.stringRes) + + updateWatchStatus(load, newValue) + reloadStored() + } + } + } + + } + } + observe(homeViewModel.apiName) { apiName -> currentApiName = apiName // setKey(USER_SELECTED_HOMEPAGE_API, apiName) 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 new file mode 100644 index 00000000..b36d7582 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollAdapter.kt @@ -0,0 +1,60 @@ +package com.lagradost.cloudstream3.ui.home + +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.viewpager.widget.PagerAdapter +import com.lagradost.cloudstream3.LoadResponse +import com.lagradost.cloudstream3.utils.UIHelper.setImage + + +class HomeScrollAdapter(private val onPrimaryCallback: (LoadResponse) -> Unit) : PagerAdapter() { + private var items: List = listOf() + + fun setItems(newItems: List) { + items = newItems + + notifyDataSetChanged() + } + + override fun getCount(): Int { + return Int.MAX_VALUE//items.size + } + + override fun getItemPosition(`object`: Any): Int { + return POSITION_NONE//super.getItemPosition(`object`) + } + + private fun getItemAtPosition(idx: Int): LoadResponse { + return items[idx % items.size] + } + + override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) { + super.setPrimaryItem(container, position, `object`) + onPrimaryCallback.invoke(getItemAtPosition(position)) + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val image = ImageView(container.context) + val item = getItemAtPosition(position) + image.scaleType = ImageView.ScaleType.CENTER_CROP + image.setImage(item.posterUrl ?: item.backgroundPosterUrl, item.posterHeaders) + + // val itemView: View = mLayoutInflater.inflate(R.layout.pager_item, container, false) + + // val imageView: ImageView = itemView.findViewById(R.id.imageView) as ImageView + // imageView.setImageResource(mResources.get(position)) + + container.addView(image) + + return image + } + + override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { + container.removeView(`object` as View) + } + + override fun isViewFromObject(view: View, `object`: Any): Boolean { + return view === `object` + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollTransformer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollTransformer.kt new file mode 100644 index 00000000..04b6964b --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeScrollTransformer.kt @@ -0,0 +1,13 @@ +package com.lagradost.cloudstream3.ui.home + +import android.view.View +import androidx.viewpager.widget.ViewPager + +class HomeScrollTransformer : ViewPager.PageTransformer { + override fun transformPage(page: View, position: Float) { + page.setPadding( + maxOf(0, (-position * page.width / 2).toInt()), 0, + maxOf(0, (position * page.width / 2).toInt()), 0 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt index af24973d..43d849cf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeViewModel.kt @@ -21,6 +21,8 @@ import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds @@ -33,6 +35,7 @@ import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.cloudstream3.utils.VideoDownloadHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.async import kotlinx.coroutines.withContext import java.util.* import kotlin.collections.set @@ -58,9 +61,9 @@ class HomeViewModel : ViewModel() { val bookmarks: LiveData>> = _bookmarks private val _resumeWatching = MutableLiveData>() - private val _preview = MutableLiveData>() + private val _preview = MutableLiveData>>() val resumeWatching: LiveData> = _resumeWatching - val preview: LiveData> = _preview + val preview: LiveData>> = _preview fun loadResumeWatching() = viewModelScope.launchSafe { val resumeWatching = withContext(Dispatchers.IO) { @@ -210,7 +213,7 @@ class HomeViewModel : ViewModel() { } - private fun load(api: MainAPI?) = viewModelScope.launchSafe { + private fun load(api: MainAPI?) = ioSafe { repo = if (api != null) { APIRepository(api) } else { @@ -236,7 +239,35 @@ class HomeViewModel : ViewModel() { ExpandableHomepageList(filteredList, 1, home.hasNext) } } + val items = data.value.mapNotNull { it?.items }.flatten() + val responses = ioWork { + items.flatMap { it.list }.shuffled().take(6).map { searchResponse -> + async { repo?.load(searchResponse.url) } + }.map { it.await() }.mapNotNull { if (it != null && it is Resource.Success) it.value else null } } + //.amap { searchResponse -> + // repo?.load(searchResponse.url) + ///} + + //.map { searchResponse -> + // async { repo?.load(searchResponse.url) } + // }.map { it.await() } + + + if (responses.isEmpty()) { + _preview.postValue( + Resource.Failure( + false, + null, + null, + "No homepage responses" + ) + ) + } else { + _preview.postValue(Resource.Success(responses)) + } + + /* items.randomOrNull()?.list?.randomOrNull()?.url?.let { url -> // backup request in case first fails var first = repo?.load(url) @@ -264,7 +295,7 @@ class HomeViewModel : ViewModel() { "No homepage items" ) ) - } + }*/ _page.postValue(Resource.Success(expandable)) 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 e20a6c7b..816b1298 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 @@ -344,7 +344,7 @@ class GeneratorPlayer : FullScreenPlayer() { seasonNumber = currentTempMeta.season, lang = currentLanguageTwoLetters.ifBlank { null } ) - val results = providers.apmap { + val results = providers.amap { try { it.search(search) } catch (e: Exception) { 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 2c8a5372..1f242481 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 @@ -1,6 +1,6 @@ package com.lagradost.cloudstream3.ui.player -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.utils.* import java.net.URI @@ -46,7 +46,7 @@ class LinkGenerator( subtitleCallback: (SubtitleData) -> Unit, offset: Int ): Boolean { - links.apmap { link -> + links.amap { link -> if (!extract || !loadExtractor(link, referer, { subtitleCallback(PlayerSubtitleHelper.getSubtitleData(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 7c099793..f424989e 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 @@ -1972,7 +1972,7 @@ class ResultViewModel2 : ViewModel() { ): List = coroutineScope { var currentCount = 0 - return@coroutineScope loadResponse.trailers.apmap { trailerData -> + return@coroutineScope loadResponse.trailers.amap { trailerData -> try { val links = arrayListOf() val subs = arrayListOf() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt index 513d68e8..91415d26 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/SyncViewModel.kt @@ -4,7 +4,7 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis @@ -197,7 +197,7 @@ class SyncViewModel : ViewModel() { /// modifies the current sync data, return null if you don't want to change it private fun modifyData(update: ((SyncAPI.SyncStatus) -> (SyncAPI.SyncStatus?))) = ioSafe { - syncs.apmap { (prefix, id) -> + syncs.amap { (prefix, id) -> repos.firstOrNull { it.idPrefix == prefix }?.let { repo -> if (repo.hasAccount()) { val result = repo.getStatus(id) 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 b629b73f..aceda644 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 @@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.SearchResponse -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.ui.APIRepository @@ -108,9 +108,9 @@ class SearchViewModel : ViewModel() { repos.filter { a -> (ignoreSettings || (providersActive.isEmpty() || providersActive.contains(a.name))) && (!isQuickSearch || a.hasQuickSearch) - }.apmap { a -> // Parallel + }.amap { a -> // Parallel val search = if (isQuickSearch) a.quickSearch(query) else a.search(query) - if (currentSearchIndex != currentIndex) return@apmap + if (currentSearchIndex != currentIndex) return@amap currentList.add(OnGoingSearch(a.name, search)) _currentSearch.postValue(currentList) } 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 897e7366..63ed5357 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 @@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModel import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.mvvm.Some import com.lagradost.cloudstream3.mvvm.debugAssert import com.lagradost.cloudstream3.plugins.PluginManager @@ -49,7 +49,7 @@ class ExtensionsViewModel : ViewModel() { val urls = (getKey>(REPOSITORIES_KEY) ?: emptyArray()) + PREBUILT_REPOSITORIES - val onlinePlugins = urls.toList().apmap { + val onlinePlugins = urls.toList().amap { RepositoryManager.getRepoPlugins(it.url)?.toList() ?: emptyList() }.flatten().distinctBy { it.second.url } 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 536dc33b..894a9331 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 @@ -10,7 +10,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.apmap +import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.getPluginPath @@ -101,7 +101,7 @@ class PluginsViewModel : ViewModel() { Toast.LENGTH_SHORT ) } - }.apmap { (repo, metadata) -> + }.amap { (repo, metadata) -> PluginManager.downloadAndLoadPlugin( activity, metadata.url, 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 08c03d6c..338b1ed2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -13,7 +13,6 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.apmap import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.PLUGINS_KEY import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL @@ -28,7 +27,6 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_S import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY -import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs import com.lagradost.cloudstream3.utils.DataStore.getSharedPrefs diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index bce49c22..3d7e3ba5 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -279,30 +279,40 @@ --> - + + android:layout_height="500dp" + android:visibility="gone" + tools:visibility="visible"> + + + + + + android:alpha="1" + android:background="@drawable/background_shadow" + android:rotation="180" + android:visibility="visible" /> + + android:layout_height="wrap_content" + android:orientation="vertical"> - + android:textSize="20dp" + android:layout_margin="20dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/search" + android:textColor="@color/white" + app:drawableLeftCompat="@drawable/search_icon" + app:tint="@color/white" /> + --> + + android:layout_gravity="center" + android:gravity="center" + android:orientation="horizontal" + android:padding="20dp"> + app:tint="?attr/white" /> + + + app:tint="?attr/white" /> @@ -522,15 +535,15 @@ + android:orientation="horizontal" + app:singleSelection="true"> diff --git a/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt b/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt index 34bd9f18..846dfdb5 100644 --- a/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt +++ b/app/src/test/java/com/lagradost/cloudstream3/ProviderTests.kt @@ -162,7 +162,7 @@ class ProviderTests { // @Test // fun providerCorrectHomepage() { // runBlocking { -// getAllProviders().apmap { api -> +// getAllProviders().amap { api -> // if (api.hasMainPage) { // try { // val homepage = api.getMainPage() @@ -197,7 +197,7 @@ class ProviderTests { // suspend fun providerCorrect() { // val invalidProvider = ArrayList>() // val providers = getAllProviders() -// providers.apmap { api -> +// providers.amap { api -> // try { // println("Trying $api") // if (testSingleProviderApi(api)) { From 7272dc67b74c0e36177222a15c3cbd20ea34bd5b Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Mon, 31 Oct 2022 12:33:09 +0100 Subject: [PATCH 042/273] fix chip tint and make adding repos by url easier --- .../cloudstream3/plugins/RepositoryManager.kt | 22 +++++++++++ .../settings/extensions/ExtensionsFragment.kt | 38 +++++++------------ app/src/main/res/values/styles.xml | 13 ++++--- 3 files changed, 43 insertions(+), 30 deletions(-) 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 2564abd0..542e2136 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt @@ -70,6 +70,28 @@ object RepositoryManager { getKey("PREBUILT_REPOSITORIES") ?: emptyArray() } + suspend fun parseRepoUrl(url: String): String? { + val fixedUrl = url.trim() + return if (fixedUrl.contains("^https?://".toRegex())) { + fixedUrl + } else if (fixedUrl.contains("^(cloudstreamrepo://)|(https://cs\\.repo/)".toRegex())) { + fixedUrl.replace("^(cloudstreamrepo://)|(https://cs\\.repo/)".toRegex(), "").let { + return@let if (!it.contains("^https?://".toRegex())) + "https://${it}" + else fixedUrl + } + } else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) { + suspendSafeApiCall { + app.get("https://l.cloudstream.cf/${fixedUrl}").let { + return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url + else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 -> + return@let2 if (it2.isSuccessful) it2.url else null + } + } + } + } else null + } + suspend fun parseRepository(url: String): Repository? { return suspendSafeApiCall { // Take manifestVersion and such into account later 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 418519e2..49f40879 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 @@ -33,8 +33,6 @@ import com.lagradost.cloudstream3.widget.LinearRecycleViewLayoutManager import kotlinx.android.synthetic.main.add_repo_input.* import kotlinx.android.synthetic.main.fragment_extensions.* -const val PUBLIC_REPOSITORIES_LIST = "https://recloudstream.github.io/repos/" - class ExtensionsFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, @@ -186,15 +184,7 @@ class ExtensionsFragment : Fragment() { (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager?)?.primaryClip?.getItemAt( 0 )?.text?.toString()?.let { copy -> - // Fix our own repo links and only paste the text if it's a link. - if (copy.startsWith("http")) { - val fixedUrl = if (copy.startsWith("https://cs.repo")) { - "https://" + copy.substringAfter("?") - } else { - copy - } - dialog.repo_url_input?.setText(fixedUrl) - } + dialog.repo_url_input?.setText(copy) } // dialog.list_repositories?.setOnClickListener { @@ -206,21 +196,21 @@ class ExtensionsFragment : Fragment() { // dialog.text2?.text = provider.name dialog.apply_btt?.setOnClickListener secondListener@{ val name = dialog.repo_name_input?.text?.toString() - val url = dialog.repo_url_input?.text?.toString() - if (url.isNullOrBlank()) { - showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT) - return@secondListener - } - ioSafe { - val fixedName = if (!name.isNullOrBlank()) name - else RepositoryManager.parseRepository(url)?.name ?: "No name" + val url = dialog.repo_url_input?.text?.toString() + ?.let { it1 -> RepositoryManager.parseRepoUrl(it1) } + if (url.isNullOrBlank()) { + showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT) + } else { + val fixedName = if (!name.isNullOrBlank()) name + else RepositoryManager.parseRepository(url)?.name ?: "No name" - val newRepo = RepositoryData(fixedName, url) - RepositoryManager.addRepository(newRepo) - extensionViewModel.loadStats() - extensionViewModel.loadRepositories() - this@ExtensionsFragment.activity?.downloadAllPluginsDialog(url, fixedName) + val newRepo = RepositoryData(fixedName, url) + RepositoryManager.addRepository(newRepo) + extensionViewModel.loadStats() + extensionViewModel.loadRepositories() + this@ExtensionsFragment.activity?.downloadAllPluginsDialog(url, fixedName) + } } dialog.dismissSafe(activity) } diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 70ac7516..f0bb2214 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -81,6 +81,7 @@ @color/transparent @color/chip_color_text @color/chip_color_text + @color/chip_color_text From c09b6881e5ac00fc7b2b9a4b096cd96e6076bde6 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Wed, 2 Nov 2022 22:56:03 +0100 Subject: [PATCH 056/273] bump --- app/build.gradle.kts | 4 ++-- .../lagradost/cloudstream3/ui/home/HomeFragment.kt | 12 ++++++++++-- app/src/main/res/layout/fragment_home.xml | 6 +++++- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 99eec423..a2c31d0b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 30 - versionCode = 52 - versionName = "3.1.6" + versionCode = 53 + versionName = "3.2.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") 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 a73fc873..ac6daa53 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 @@ -74,8 +74,10 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectSt import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount +import com.lagradost.cloudstream3.utils.UIHelper.getStatusBarHeight import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur @@ -548,7 +550,13 @@ class HomeFragment : Fragment() { observe(homeViewModel.preview) { preview -> // Always reset the padding, otherwise the will move lower and lower - home_watch_holder?.setPadding(0, 0, 0, 0) + // home_fix_padding?.setPadding(0, 0, 0, 0) + home_fix_padding?.let { v -> + val params = v.layoutParams + params.height = 0 + v.layoutParams = params + } + when (preview) { is Resource.Success -> { home_preview?.isVisible = true @@ -569,7 +577,7 @@ class HomeFragment : Fragment() { false ) home_preview?.isVisible = false - context?.fixPaddingStatusbar(home_watch_holder) + context?.fixPaddingStatusbarView(home_fix_padding) } } } diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 4eb7b9ee..e6270a77 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -405,7 +405,11 @@ - + + + android:id="@+id/player_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + app:auto_show="true" + app:backgroundTint="@android:color/black" + app:controller_layout_id="@layout/player_custom_layout" + app:hide_on_touch="false" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:show_timeout="0" /> + android:id="@+id/player_loading_overlay" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/black" + android:backgroundTint="@android:color/black" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:text="@string/skip_loading" + android:textAllCaps="false" + android:textColor="?attr/textColor" + android:visibility="gone" + app:cornerRadius="4dp" + app:icon="@drawable/ic_baseline_skip_next_24" + app:iconTint="?attr/textColor" + app:rippleColor="?attr/colorPrimary" + tools:visibility="visible" /> + android:id="@+id/main_load" + android:layout_width="50dp" + android:layout_height="50dp" + android:layout_gravity="center" /> + android:id="@+id/video_go_back_holder_holder" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="5dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:contentDescription="@string/go_back_img_des" + android:src="@drawable/ic_baseline_arrow_back_24" + app:tint="@android:color/white" /> + android:id="@+id/player_loading_go_back" + android:layout_width="70dp" + android:layout_height="70dp" + android:layout_gravity="center" + android:background="@drawable/video_tap_button_always_white" + android:clickable="true" + android:contentDescription="@string/go_back_img_des" + android:focusable="true" /> + + + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:gravity="start" + android:textColor="@color/white" + android:textStyle="bold" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="78% at 18kb/s" /> - - + android:id="@+id/video_torrent_seeders" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="0dp" + android:gravity="start" + android:textColor="@color/white" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toBottomOf="@+id/player_video_title" + tools:text="17 seeders" /> \ No newline at end of file diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 9bbded4e..2cdcbf22 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -318,6 +318,25 @@ + + #66000000 #C0121212 + #4D121212 #121212 #66B5B5B5 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5665ea5c..144d2477 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -638,4 +638,13 @@ Browser App not found All Languages + + Skip %s + Opening + Ending + Recap + Mixed ending + Mixed opening + Credits + Intro From 9e67e856a0d12d326bcab23edc5925c881f9a496 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 5 Nov 2022 00:40:31 +0100 Subject: [PATCH 061/273] small fix for multi request --- .../com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 e1f1d99d..f4d2cd2f 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 @@ -166,7 +166,8 @@ class GeneratorPlayer : FullScreenPlayer() { isActive = true setPlayerDimen(null) setTitle() - hasRequestedStamps = false + if (!sameEpisode) + hasRequestedStamps = false loadExtractorJob(link.first) // load player @@ -186,7 +187,8 @@ class GeneratorPlayer : FullScreenPlayer() { ), ) } - player.addTimeStamps(listOf()) // clear stamps + if (!sameEpisode) + player.addTimeStamps(listOf()) // clear stamps } private fun sortLinks(useQualitySettings: Boolean = true): List> { From 60a2f7c1c51456d025f92c191b51cf38e411ae21 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 5 Nov 2022 01:27:35 +0100 Subject: [PATCH 062/273] clear history --- .../cloudstream3/ui/search/SearchFragment.kt | 39 ++++++++++++++++++- app/src/main/res/drawable/delete_all.xml | 9 +++++ app/src/main/res/layout/fragment_search.xml | 19 +++++++++ app/src/main/res/values/strings.xml | 3 ++ 4 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/delete_all.xml 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 bcb36f7e..e6e290c1 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 @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.ui.search +import android.content.DialogInterface import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater @@ -10,6 +11,7 @@ import android.widget.AbsListView import android.widget.ArrayAdapter import android.widget.ImageView import android.widget.ListView +import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import androidx.fragment.app.Fragment @@ -28,6 +30,7 @@ import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiSettings import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.mvvm.Resource @@ -343,7 +346,7 @@ class SearchFragment : Fragment() { searchViewModel.updateHistory() } - search_history_recycler?.isVisible = showHistory + search_history_holder?.isVisible = showHistory search_master_recycler?.isVisible = !showHistory && isAdvancedSearch search_autofit_results?.isVisible = !showHistory && !isAdvancedSearch @@ -352,7 +355,41 @@ class SearchFragment : Fragment() { } }) + search_clear_call_history?.setOnClickListener { + activity?.let { ctx -> + val builder: AlertDialog.Builder = AlertDialog.Builder(ctx) + val dialogClickListener = + DialogInterface.OnClickListener { _, which -> + when (which) { + DialogInterface.BUTTON_POSITIVE -> { + removeKeys(SEARCH_HISTORY_KEY) + searchViewModel.updateHistory() + } + DialogInterface.BUTTON_NEGATIVE -> { + } + } + } + + try { + builder.setTitle(R.string.clear_history).setMessage( + ctx.getString(R.string.delete_message).format( + ctx.getString(R.string.history) + ) + ) + .setPositiveButton(R.string.sort_clear, dialogClickListener) + .setNegativeButton(R.string.cancel, dialogClickListener) + .show() + } catch (e: Exception) { + logError(e) + // ye you somehow fucked up formatting did you? + } + } + + + } + observe(searchViewModel.currentHistory) { list -> + search_clear_call_history?.isVisible = list.isNotEmpty() (search_history_recycler.adapter as? SearchHistoryAdaptor?)?.updateList(list) } diff --git a/app/src/main/res/drawable/delete_all.xml b/app/src/main/res/drawable/delete_all.xml new file mode 100644 index 00000000..a5e8775c --- /dev/null +++ b/app/src/main/res/drawable/delete_all.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 083cd64e..6df5837f 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -112,6 +112,11 @@ android:nextFocusLeft="@id/nav_rail_view" android:visibility="gone" tools:listitem="@layout/homepage_parent" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 144d2477..e3ef9f6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -647,4 +647,7 @@ Mixed opening Credits Intro + + Clear history + History From 60bcbf0060750f661f5ae53f197b29b71eae365c Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 5 Nov 2022 19:54:04 +0100 Subject: [PATCH 063/273] A lil cache fixing & search fixes --- app/build.gradle.kts | 6 +++--- .../main/java/com/lagradost/cloudstream3/CommonActivity.kt | 2 ++ .../com/lagradost/cloudstream3/ui/search/SearchFragment.kt | 6 +++--- .../ui/settings/extensions/ExtensionsFragment.kt | 4 +++- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4a1fcada..f72eb321 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 30 - versionCode = 54 - versionName = "3.2.2" + versionCode = 55 + versionName = "3.2.3" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") @@ -190,7 +190,7 @@ dependencies { // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") - implementation("com.github.Blatzar:NiceHttp:0.3.4") + implementation("com.github.Blatzar:NiceHttp:0.3.5") // Util to skip the URI file fuckery 🙏 implementation("com.github.tachiyomiorg:unifile:17bec43") diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index af04a1a2..342dc6f7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -61,6 +61,7 @@ object CommonActivity { } } + @MainThread fun showToast(act: Activity?, @StringRes message: Int, duration: Int) { if (act == null) return showToast(act, act.getString(message), duration) @@ -69,6 +70,7 @@ object CommonActivity { const val TAG = "COMPACT" /** duration is Toast.LENGTH_SHORT if null*/ + @MainThread fun showToast(act: Activity?, message: String?, duration: Int? = null) { if (act == null || message == null) { Log.w(TAG, "invalid showToast act = $act message = $message") 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 e6e290c1..4da88af7 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 @@ -131,9 +131,9 @@ class SearchFragment : Fragment() { context?.let { ctx -> val default = enumValues().sorted().filter { it != TvType.NSFW } .map { it.ordinal.toString() }.toSet() - val preferredTypes = PreferenceManager.getDefaultSharedPreferences(ctx) - .getStringSet(this.getString(R.string.prefer_media_type_key), default) - ?.mapNotNull { it.toIntOrNull() ?: return@mapNotNull null } ?: default + val preferredTypes = (PreferenceManager.getDefaultSharedPreferences(ctx) + .getStringSet(this.getString(R.string.prefer_media_type_key), default)?.ifEmpty { default } ?: default) + .mapNotNull { it.toIntOrNull() ?: return@mapNotNull null } val settings = ctx.getApiSettings() 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 49f40879..fbf10499 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 @@ -200,7 +200,9 @@ class ExtensionsFragment : Fragment() { val url = dialog.repo_url_input?.text?.toString() ?.let { it1 -> RepositoryManager.parseRepoUrl(it1) } if (url.isNullOrBlank()) { - showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT) + main { + showToast(activity, R.string.error_invalid_data, Toast.LENGTH_SHORT) + } } else { val fixedName = if (!name.isNullOrBlank()) name else RepositoryManager.parseRepository(url)?.name ?: "No name" From 4fb65e724290a5a130f10c665e45acf0c8f20a99 Mon Sep 17 00:00:00 2001 From: Sdarfeesh <50188628+Sdarfeesh@users.noreply.github.com> Date: Sun, 6 Nov 2022 04:55:41 +0800 Subject: [PATCH 064/273] Update Simplified Chinese Translation (#198) --- app/src/main/res/values-zh/strings.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 8f436fe9..a9cc91b7 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -577,4 +577,17 @@ 投屏 浏览器 未找到应用 + 所有语言 + + 跳过 %s + 片头 + 片尾 + 前情回顾 + 混合片尾 + 混合片头 + 致谢名单 + 介绍 + + 清除历史记录 + 历史记录 From 8fa00f4ca902683430d1f12f128edb645a09e78d Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 5 Nov 2022 22:15:34 +0100 Subject: [PATCH 065/273] added setting for skip op and changed key for player seek amount --- .../cloudstream3/ui/player/GeneratorPlayer.kt | 11 +- app/src/main/res/values/strings.xml | 5 +- app/src/main/res/xml/settings_player.xml | 190 +++++++++--------- 3 files changed, 112 insertions(+), 94 deletions(-) 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 f4d2cd2f..95a9393f 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 @@ -187,6 +187,7 @@ class GeneratorPlayer : FullScreenPlayer() { ), ) } + if (!sameEpisode) player.addTimeStamps(listOf()) // clear stamps } @@ -874,7 +875,15 @@ class GeneratorPlayer : FullScreenPlayer() { if (duration <= 0L) return // idk how you achieved this, but div by zero crash if (!hasRequestedStamps) { hasRequestedStamps = true - viewModel.loadStamps(duration) + val fetchStamps = context?.let { ctx -> + val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) + settingsManager.getBoolean( + ctx.getString(R.string.enable_skip_op_from_database), + true + ) + } ?: true + if (fetchStamps) + viewModel.loadStamps(duration) } viewModel.getId()?.let { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3ef9f6b..e2ebf0ab 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,7 +28,7 @@ pip_enabled_key double_tap_enabled_key double_tap_pause_enabled_key - double_tap_seek_time_key + double_tap_seek_time_key2 swipe_vertical_enabled_key autoplay_next_key display_sub_key @@ -57,6 +57,7 @@ filter_sub_lang_key pref_filter_search_quality_key enable_nsfw_on_providers_key + enable_skip_op_from_database %d %s | %s @@ -650,4 +651,6 @@ Clear history History + Show skip popups for opening/ending + diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml index 6946a5c9..0be6313f 100644 --- a/app/src/main/res/xml/settings_player.xml +++ b/app/src/main/res/xml/settings_player.xml @@ -1,126 +1,132 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:icon="@drawable/ic_outline_subtitles_24" + android:key="@string/subtitle_settings_key" + android:title="@string/player_subtitles_settings" + app:summary="@string/player_subtitles_settings_des" /> + android:icon="@drawable/ic_outline_subtitles_24" + android:key="@string/subtitle_settings_chromecast_key" + android:title="@string/chromecast_subtitles_settings" + app:summary="@string/chromecast_subtitles_settings_des" /> + android:icon="@drawable/ic_baseline_hd_24" + android:key="@string/quality_pref_key" + android:title="@string/watch_quality_pref" /> + android:icon="@drawable/netflix_play" + android:key="@string/player_pref_key" + android:title="@string/player_pref" /> + android:icon="@drawable/ic_baseline_text_format_24" + android:key="@string/prefer_limit_title_key" + android:title="@string/limit_title" /> + android:icon="@drawable/ic_baseline_text_format_24" + android:key="@string/prefer_limit_title_rez_key" + android:title="@string/limit_title_rez" /> + android:icon="@drawable/ic_baseline_picture_in_picture_alt_24" + android:summary="@string/picture_in_picture_des" + android:title="@string/picture_in_picture" + app:defaultValue="true" + app:key="@string/pip_enabled_key" /> + android:icon="@drawable/ic_baseline_aspect_ratio_24" + android:summary="@string/player_size_settings_des" + android:title="@string/player_size_settings" + app:defaultValue="true" + app:key="@string/player_resize_enabled_key" /> + android:icon="@drawable/ic_baseline_speed_24" + android:summary="@string/eigengraumode_settings_des" + android:title="@string/eigengraumode_settings" + app:defaultValue="false" + app:key="@string/playback_speed_enabled_key" /> + android:icon="@drawable/ic_baseline_ondemand_video_24" + android:summary="@string/swipe_to_seek_settings_des" + android:title="@string/swipe_to_seek_settings" + app:defaultValue="true" + app:key="@string/swipe_enabled_key" /> - + android:icon="@drawable/ic_baseline_ondemand_video_24" + android:summary="@string/swipe_to_change_settings_des" + android:title="@string/swipe_to_change_settings" + app:defaultValue="true" + app:key="@string/swipe_vertical_enabled_key" /> + android:icon="@drawable/ic_baseline_skip_next_24" + android:summary="@string/autoplay_next_settings_des" + android:title="@string/autoplay_next_settings" + app:defaultValue="true" + app:key="@string/autoplay_next_key" /> + android:icon="@drawable/ic_baseline_skip_next_24" + android:title="@string/video_skip_op" + app:defaultValue="true" + android:summary="@string/enable_skip_op_from_database_des" + app:key="@string/enable_skip_op_from_database" /> + + + android:defaultValue="10" + android:max="60" + android:title="@string/double_tap_to_seek_amount_settings" + app:adjustable="true" + app:defaultValue="10" + app:icon="@drawable/go_forward_30" + app:key="@string/double_tap_seek_time_key" + app:min="5" + app:seekBarIncrement="5" + app:showSeekBarValue="true" /> + android:icon="@drawable/baseline_sync_24" + android:summary="@string/episode_sync_settings_des" + android:title="@string/episode_sync_settings" + app:defaultValue="true" + app:key="@string/episode_sync_enabled_key" /> + android:icon="@drawable/ic_baseline_storage_24" + android:key="@string/video_buffer_disk_key" + android:summary="@string/video_disk_description" + android:title="@string/video_buffer_disk_settings" /> + android:icon="@drawable/ic_baseline_storage_24" + android:key="@string/video_buffer_size_key" + android:summary="@string/video_ram_description" + android:title="@string/video_buffer_size_settings" /> + android:icon="@drawable/ic_baseline_storage_24" + android:key="@string/video_buffer_length_key" + android:summary="@string/video_ram_description" + android:title="@string/video_buffer_length_settings" /> + android:icon="@drawable/ic_baseline_delete_outline_24" + android:key="@string/video_buffer_clear_key" + android:title="@string/video_buffer_clear_settings" /> \ No newline at end of file From aef6f93efe212837e0009035c082c6ea3f2bcb08 Mon Sep 17 00:00:00 2001 From: darkdemon Date: Sun, 6 Nov 2022 16:43:45 +0530 Subject: [PATCH 066/273] - Open all extractor classes - Add StreamTape clones[Streamtape.net, ShaveTape.cash] - optimize imports for AstreamHub, GMplayer --- .../lagradost/cloudstream3/extractors/AStreamHub.kt | 7 ++++--- .../lagradost/cloudstream3/extractors/Acefile.kt | 2 +- .../lagradost/cloudstream3/extractors/AsianLoad.kt | 2 +- .../lagradost/cloudstream3/extractors/Blogger.kt | 2 +- .../lagradost/cloudstream3/extractors/BullStream.kt | 2 +- .../lagradost/cloudstream3/extractors/Embedgram.kt | 2 +- .../lagradost/cloudstream3/extractors/Fastream.kt | 2 +- .../lagradost/cloudstream3/extractors/Filesim.kt | 2 +- .../lagradost/cloudstream3/extractors/GMPlayer.kt | 3 +-- .../lagradost/cloudstream3/extractors/Linkbox.kt | 2 +- .../com/lagradost/cloudstream3/extractors/Mcloud.kt | 7 ------- .../lagradost/cloudstream3/extractors/Mp4Upload.kt | 2 +- .../cloudstream3/extractors/MultiQuality.kt | 2 +- .../com/lagradost/cloudstream3/extractors/Mvidoo.kt | 2 +- .../lagradost/cloudstream3/extractors/Pelisplus.kt | 2 +- .../lagradost/cloudstream3/extractors/PlayLtXyz.kt | 2 +- .../lagradost/cloudstream3/extractors/Solidfiles.kt | 2 +- .../lagradost/cloudstream3/extractors/StreamTape.kt | 13 +++++++++++-- .../lagradost/cloudstream3/extractors/Streamhub.kt | 2 +- .../lagradost/cloudstream3/extractors/Streamplay.kt | 2 +- .../lagradost/cloudstream3/extractors/Supervideo.kt | 11 ++++++----- .../cloudstream3/extractors/UpstreamExtractor.kt | 2 +- .../lagradost/cloudstream3/extractors/VideoVard.kt | 2 +- .../lagradost/cloudstream3/extractors/WcoStream.kt | 6 ++++++ .../lagradost/cloudstream3/extractors/YourUpload.kt | 2 +- .../lagradost/cloudstream3/extractors/Zorofile.kt | 2 +- .../lagradost/cloudstream3/utils/ExtractorApi.kt | 2 ++ 27 files changed, 50 insertions(+), 39 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt index 18602664..b0051ba7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/AStreamHub.kt @@ -2,10 +2,11 @@ package com.lagradost.cloudstream3.extractors import android.util.Log import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.base64Decode -import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities -class AStreamHub : ExtractorApi() { +open class AStreamHub : ExtractorApi() { override val name = "AStreamHub" override val mainUrl = "https://astreamhub.com" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Acefile.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Acefile.kt index fe46791b..18198f44 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Acefile.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Acefile.kt @@ -4,7 +4,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.base64Decode import com.lagradost.cloudstream3.utils.* -class Acefile : ExtractorApi() { +open class Acefile : ExtractorApi() { override val name = "Acefile" override val mainUrl = "https://acefile.co" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt index cf16f200..7a62fb52 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/AsianLoad.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.M3u8Helper import com.lagradost.cloudstream3.utils.getQualityFromName import java.net.URI -class AsianLoad : ExtractorApi() { +open class AsianLoad : ExtractorApi() { override var name = "AsianLoad" override var mainUrl = "https://asianembed.io" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Blogger.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Blogger.kt index cae77322..44e700b1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Blogger.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Blogger.kt @@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -class Blogger : ExtractorApi() { +open class Blogger : ExtractorApi() { override val name = "Blogger" override val mainUrl = "https://www.blogger.com" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/BullStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/BullStream.kt index e5368bc3..71fa7066 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/BullStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/BullStream.kt @@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper -class BullStream : ExtractorApi() { +open class BullStream : ExtractorApi() { override val name = "BullStream" override val mainUrl = "https://bullstream.xyz" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Embedgram.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Embedgram.kt index 35569663..45a06dcc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Embedgram.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Embedgram.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName import com.lagradost.cloudstream3.utils.httpsify -class Embedgram : ExtractorApi() { +open class Embedgram : ExtractorApi() { override val name = "Embedgram" override val mainUrl = "https://embedgram.com" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt index 0d5a5c78..f813d7ea 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 -class Fastream: ExtractorApi() { +open class Fastream: ExtractorApi() { override var mainUrl = "https://fastream.to" override var name = "Fastream" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 5c8af1c5..8e3dc730 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -class Filesim : ExtractorApi() { +open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt index af02ee8a..52c45096 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GMPlayer.kt @@ -3,10 +3,9 @@ package com.lagradost.cloudstream3.extractors 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.Qualities -class GMPlayer : ExtractorApi() { +open class GMPlayer : ExtractorApi() { override val name = "GM Player" override val mainUrl = "https://gmplayer.xyz" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt index 52fc5532..73734e2a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName -class Linkbox : ExtractorApi() { +open class Linkbox : ExtractorApi() { override val name = "Linkbox" override val mainUrl = "https://www.linkbox.to" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt deleted file mode 100644 index 29d98557..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mcloud.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.lagradost.cloudstream3.extractors - -open class Mcloud : WcoStream() { - override var name = "Mcloud" - override var mainUrl = "https://mcloud.to" - override val requiresReferer = true -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mp4Upload.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mp4Upload.kt index 68a4a103..93a280ed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mp4Upload.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mp4Upload.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.getAndUnpack -class Mp4Upload : ExtractorApi() { +open class Mp4Upload : ExtractorApi() { override var name = "Mp4Upload" override var mainUrl = "https://www.mp4upload.com" private val srcRegex = Regex("""player\.src\("(.*?)"""") diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/MultiQuality.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/MultiQuality.kt index 0c0b5c68..44657196 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/MultiQuality.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/MultiQuality.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.getQualityFromName import java.net.URI -class MultiQuality : ExtractorApi() { +open class MultiQuality : ExtractorApi() { override var name = "MultiQuality" override var mainUrl = "https://gogo-play.net" private val sourceRegex = Regex("""file:\s*['"](.*?)['"],label:\s*['"](.*?)['"]""") diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mvidoo.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mvidoo.kt index c00df942..9e5f5e74 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Mvidoo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Mvidoo.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities -class Mvidoo : ExtractorApi() { +open class Mvidoo : ExtractorApi() { override val name = "Mvidoo" override val mainUrl = "https://mvidoo.com" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt index de469b22..45ec4c2f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Pelisplus.kt @@ -14,7 +14,7 @@ import org.jsoup.Jsoup * overrideMainUrl is necessary for for other vidstream clones like vidembed.cc * If they diverge it'd be better to make them separate. * */ -class Pelisplus(val mainUrl: String) { +open class Pelisplus(val mainUrl: String) { val name: String = "Vidstream" private fun getExtractorUrl(id: String): String { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt index 9a031556..2b286abb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/PlayLtXyz.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -class PlayLtXyz: ExtractorApi() { +open class PlayLtXyz: ExtractorApi() { override val name: String = "PlayLt" override val mainUrl: String = "https://play.playlt.xyz" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Solidfiles.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Solidfiles.kt index 849f5fc8..cc34781c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Solidfiles.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Solidfiles.kt @@ -8,7 +8,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName -class Solidfiles : ExtractorApi() { +open class Solidfiles : ExtractorApi() { override val name = "Solidfiles" override val mainUrl = "https://www.solidfiles.com" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamTape.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamTape.kt index af436ff3..ece8dc4b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamTape.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamTape.kt @@ -5,7 +5,15 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities -class StreamTape : ExtractorApi() { +class StreamTapeNet : StreamTape() { + override var mainUrl = "https://streamtape.net" +} + +class ShaveTape : StreamTape(){ + override var mainUrl = "https://shavetape.cash" +} + +open class StreamTape : ExtractorApi() { override var name = "StreamTape" override var mainUrl = "https://streamtape.com" override val requiresReferer = false @@ -16,7 +24,8 @@ class StreamTape : ExtractorApi() { override suspend fun getUrl(url: String, referer: String?): List? { with(app.get(url)) { linkRegex.find(this.text)?.let { - val extractedUrl = "https:${it.groups[1]!!.value + it.groups[2]!!.value.substring(3,)}" + val extractedUrl = + "https:${it.groups[1]!!.value + it.groups[2]!!.value.substring(3)}" return listOf( ExtractorLink( name, diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamhub.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamhub.kt index 2765ae17..c7689c58 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamhub.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamhub.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.JsUnpacker import com.lagradost.cloudstream3.utils.Qualities import java.net.URI -class Streamhub : ExtractorApi() { +open class Streamhub : ExtractorApi() { override var mainUrl = "https://streamhub.to" override var name = "Streamhub" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamplay.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamplay.kt index 3f5e5bd6..e6bbfeba 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamplay.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Streamplay.kt @@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import java.net.URI -class Streamplay : ExtractorApi() { +open class Streamplay : ExtractorApi() { override val name = "Streamplay" override val mainUrl = "https://streamplay.to" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Supervideo.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Supervideo.kt index 955345aa..dd49d994 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Supervideo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Supervideo.kt @@ -11,7 +11,7 @@ data class Files( @JsonProperty("label") val label: String? = null, ) - open class Supervideo : ExtractorApi() { +open class Supervideo : ExtractorApi() { override var name = "Supervideo" override var mainUrl = "https://supervideo.tv" override val requiresReferer = false @@ -20,10 +20,13 @@ data class Files( val response = app.get(url).text val jstounpack = Regex("eval((.|\\n)*?)").find(response)?.groups?.get(1)?.value val unpacjed = JsUnpacker(jstounpack).unpack() - val extractedUrl = unpacjed?.let { Regex("""sources:((.|\n)*?)image""").find(it) }?.groups?.get(1)?.value.toString().replace("file",""""file"""").replace("label",""""label"""").substringBeforeLast(",") + val extractedUrl = + unpacjed?.let { Regex("""sources:((.|\n)*?)image""").find(it) }?.groups?.get(1)?.value.toString() + .replace("file", """"file"""").replace("label", """"label"""") + .substringBeforeLast(",") val parsedlinks = parseJson>(extractedUrl) parsedlinks.forEach { data -> - if (data.label.isNullOrBlank()){ // mp4 links (with labels) are slow. Use only m3u8 link. + if (data.label.isNullOrBlank()) { // mp4 links (with labels) are slow. Use only m3u8 link. M3u8Helper.generateM3u8( name, data.id, @@ -34,8 +37,6 @@ data class Files( } } } - - return extractedLinksList } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/UpstreamExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/UpstreamExtractor.kt index 1eb384c4..09e47d03 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/UpstreamExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/UpstreamExtractor.kt @@ -6,7 +6,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper -class UpstreamExtractor : ExtractorApi() { +open class UpstreamExtractor : ExtractorApi() { override val name: String = "Upstream" override val mainUrl: String = "https://upstream.to" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoVard.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoVard.kt index 41e77967..30a1d8fe 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoVard.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VideoVard.kt @@ -11,7 +11,7 @@ class VideovardSX : WcoStream() { override var mainUrl = "https://videovard.sx" } -class VideoVard : ExtractorApi() { +open class VideoVard : ExtractorApi() { override var name = "Videovard" // Cause works for animekisa and wco override var mainUrl = "https://videovard.to" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt index d99485ea..6cc486cd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/WcoStream.kt @@ -53,6 +53,12 @@ class VizcloudSite : WcoStream() { override var mainUrl = "https://vizcloud.site" } +class Mcloud : WcoStream() { + override var name = "Mcloud" + override var mainUrl = "https://mcloud.to" + override val requiresReferer = true +} + open class WcoStream : ExtractorApi() { override var name = "VidStream" // Cause works for animekisa and wco override var mainUrl = "https://vidstream.pro" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/YourUpload.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/YourUpload.kt index 3c564f67..c7aa989d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/YourUpload.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/YourUpload.kt @@ -7,7 +7,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.getQualityFromName -class YourUpload: ExtractorApi() { +open class YourUpload: ExtractorApi() { override val name = "Yourupload" override val mainUrl = "https://www.yourupload.com" override val requiresReferer = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zorofile.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zorofile.kt index 84785b6c..43c4eefb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Zorofile.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Zorofile.kt @@ -10,7 +10,7 @@ import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper -class Zorofile : ExtractorApi() { +open class Zorofile : ExtractorApi() { override val name = "Zorofile" override val mainUrl = "https://zorofile.com" override val requiresReferer = true diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 1a1afb68..24708e99 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -205,6 +205,8 @@ val extractorApis: MutableList = arrayListOf( VideovardSX(), Mp4Upload(), StreamTape(), + StreamTapeNet(), + ShaveTape(), //mixdrop extractors MixDropBz(), From 1226426389ac327514f3189826b8712516dd9270 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 6 Nov 2022 20:16:48 +0100 Subject: [PATCH 067/273] More crash fixes --- .../lagradost/cloudstream3/CommonActivity.kt | 3 ++- .../com/lagradost/cloudstream3/MainAPI.kt | 11 +++++++++-- .../ui/settings/SettingsUpdates.kt | 19 +++++++++++++------ app/src/main/res/values/strings.xml | 2 +- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 342dc6f7..47a195d1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -61,8 +61,9 @@ object CommonActivity { } } + /** duration is Toast.LENGTH_SHORT if null*/ @MainThread - fun showToast(act: Activity?, @StringRes message: Int, duration: Int) { + fun showToast(act: Activity?, @StringRes message: Int, duration: Int? = null) { if (act == null) return showToast(act, act.getString(message), duration) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 4b271192..5c9f3071 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -81,7 +81,8 @@ object APIHolder { synchronized(allProviders) { initMap() return apiMap?.get(apiName)?.let { apis.getOrNull(it) } - ?: allProviders.firstOrNull { it.name == apiName } + // Leave the ?. null check, it can crash regardless + ?: allProviders.firstOrNull { it?.name == apiName } } } @@ -244,11 +245,17 @@ object APIHolder { fun Context.filterProviderByPreferredMedia(hasHomePageIsRequired: Boolean = true): List { // We are getting the weirdest crash ever done: // java.lang.ClassCastException: com.lagradost.cloudstream3.TvType cannot be cast to com.lagradost.cloudstream3.TvType - // enumValues() might be the cause, hence I am trying TvType.values() + // Trying fixing using classloader fuckery + val oldLoader = Thread.currentThread().contextClassLoader + Thread.currentThread().contextClassLoader = TvType::class.java.classLoader + val default = TvType.values() .sorted() .filter { it != TvType.NSFW } .map { it.ordinal } + + Thread.currentThread().contextClassLoader = oldLoader + val defaultSet = default.map { it.toString() }.toSet() val currentPrefMedia = try { PreferenceManager.getDefaultSharedPreferences(this) 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 6b19042a..78f6d628 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 @@ -4,12 +4,14 @@ import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.os.Bundle +import android.os.TransactionTooLargeException import android.view.View import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.navigation.fragment.findNavController import androidx.preference.PreferenceFragmentCompat import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref @@ -81,12 +83,17 @@ class SettingsUpdates : PreferenceFragmentCompat() { dialog.text1?.text = text dialog.copy_btt?.setOnClickListener { - val serviceClipboard = - (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) - ?: return@setOnClickListener - val clip = ClipData.newPlainText("logcat", text) - serviceClipboard.setPrimaryClip(clip) - dialog.dismissSafe(activity) + // Can crash on too much text + try { + val serviceClipboard = + (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) + ?: return@setOnClickListener + val clip = ClipData.newPlainText("logcat", text) + serviceClipboard.setPrimaryClip(clip) + dialog.dismissSafe(activity) + } catch (e: TransactionTooLargeException) { + showToast(activity, R.string.clipboard_too_large) + } } dialog.clear_btt?.setOnClickListener { Runtime.getRuntime().exec("logcat -c") diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e2ebf0ab..9381372c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -652,5 +652,5 @@ Clear history History Show skip popups for opening/ending - + Too much text. Unable to save to clipboard. From e95d117ebcb5f6821d53be9f4cad6bd997be23f1 Mon Sep 17 00:00:00 2001 From: Sdarfeesh <50188628+Sdarfeesh@users.noreply.github.com> Date: Mon, 7 Nov 2022 18:49:53 +0800 Subject: [PATCH 068/273] Update Simplified Chinese Translation (#199) --- app/src/main/res/values-zh/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a9cc91b7..19d9bd43 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -590,4 +590,6 @@ 清除历史记录 历史记录 + 显示跳过片头/片尾弹窗 + 文本过多,无法保存到剪贴板。 From 2e7823034bc99768e119abd44131e55ba1240cc5 Mon Sep 17 00:00:00 2001 From: Davide <49226282+pizidavi@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:21:56 +0100 Subject: [PATCH 069/273] Updated Italian translation (#206) --- app/src/main/res/values-it/strings.xml | 61 +++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 86206213..fa1653c6 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -153,6 +153,10 @@ Scorri a sinistra o a destra per controllare il tempo del video Scorri per cambiare le impostazioni Passa il dito sul lato sinistro o destro per cambiare la luminosità o il volume + + Riproduci automaticamente l\'episodio successivo + Avvia l\'episodio successivo al termine di quello in corso + Doppio tocco per andare avanti/indietro Doppio tocco per mettere in pausa Tocca due volte il lato destro o sinistro dello schermo per mandare avanti o indietro il video @@ -176,7 +180,7 @@ Cerca Accounts Aggiornamenti e Backup - + Info Ricerca avanzata Dividi i risultati della ricerca per provider @@ -185,7 +189,9 @@ Mostra tag [filler] per anime Mostra trailer Mostra poster da Kitsu + Nascondi la qualità video selezionata dai risultati di ricerca + Aggiorna automaticamente i plugin Mostra gli aggiornamenti dell\'app Cerca automaticamente nuovi aggiornamenti all\'avvio Aggiorna alle prerelease @@ -207,9 +213,12 @@ Spiacente, l\'applicazione è andata in crash. Una segnalazione anonima di bug sarà inviata agli sviluppatori Stagione + %s %d%s Nessuna stagione Episodio Episodi + %d-%d + %d %s S E Nessun episodio trovato @@ -250,6 +259,8 @@ Documentari Drama Asiatici Livestreams + NSFW + Altri Film @@ -261,6 +272,8 @@ Documentario Drama Asiatico Livestream + NSFW + Altro Source error Remote error @@ -332,7 +345,9 @@ Lingua provider Layout app Media preferito + Abilita NSFW sui provider supportati Encoding Sottotitoli + Provider Interfaccia utente Auto @@ -437,11 +452,11 @@ Errore - + Filtra in base alla lingua preferita + Extra Trailer Link allo stream - - + Referer Prossimo Guarda video in queste lingue @@ -471,6 +486,7 @@ Scaricato: %d Disabilitato: %d Non scaricato: %d + Aggiornati %d plugin Aggiungi una repository per installare l\'estensione dei siti Vedi le repository della community Lista pubblica @@ -484,10 +500,41 @@ Applica al riavvio Safe Mode abilitata - Si è verificato un crash irrecuperabile e abbiamo disabilitato automaticamente tutte le estensioni, in modo che possiate trovare e rimuovere l\'estensione che causa il problema. + Si è verificato un crash irrecuperabile. Tutte le estensioni sono state disabilitate, in modo da trovare e rimuovere l\'estensione che causa il problema. Vedi informazioni del crash + + Voto: %s + Descrizione + Versione + Stato + Dimensione + Autori + Supportati + Lingua + Prima installa l\'estensione + HLS Playlist - + Video player preferito + Player interno + VLC + MPV + Web Video Cast + Browser + App non trovata + Tutte le lingue + + Salta %s + + + Riassunto + + + Crediti + + + Cancella cronologia + Cronologia + Mostra popup per salta sigla iniziale/finale + Testo troppo lungo. Impossibile salvare negli appunti. From 639de891c6009e5b04650907cd0fccc268fbda8f Mon Sep 17 00:00:00 2001 From: SANCTI-afk <63229113+SANCTI-afk@users.noreply.github.com> Date: Fri, 11 Nov 2022 17:22:13 +0200 Subject: [PATCH 070/273] Arabic language minor update (#204) --- app/src/main/res/values-ar/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index edac2760..bda3cf2a 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -20,7 +20,7 @@ CloudStream تشغيل بواسطة CloudStream - الصفحة الرئيسية + الرئيسية البحث التنزيلات الإعدادات @@ -31,8 +31,8 @@ لايوجد بيانات المزيد من الخيارات الحلقة التالية - أنواع - شارك + النوع + مشاركة فتح في الويب تخطي التحميل …تحميل @@ -230,7 +230,7 @@ تقييم المدة الزمنية موقع - ملخص + القصة في قائمة الانتظار الترجمة ليست موجودة From 81adb10c1f4111c51819b1f3d0f716d1f84d72b1 Mon Sep 17 00:00:00 2001 From: TubaApollo <86665265+TubaApollo@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:22:26 +0100 Subject: [PATCH 071/273] german translation update (#210) --- app/src/main/res/values-de/strings-de.xml | 53 +++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/app/src/main/res/values-de/strings-de.xml b/app/src/main/res/values-de/strings-de.xml index e1d657c7..065990c5 100644 --- a/app/src/main/res/values-de/strings-de.xml +++ b/app/src/main/res/values-de/strings-de.xml @@ -19,11 +19,11 @@ CloudStream Mit CloudStream abspielen Startseite - Suche + Suchen Downloads Einstellungen - Suche… + Suchen… Suche %s… Keine Daten vorhanden @@ -32,15 +32,15 @@ Genres Teilen In Browser öffnen - Buffern überspringen + Puffern überspringen Lädt… Am schauen Pausiert Abgeschlossen Abgebrochen - Möchte schauen - None + Geplant + Nichts Erneut anschauen Film abspielen @@ -90,7 +90,7 @@ Löschen Speichern - Players Geschwindigkeit + Player Geschwindigkeit Untertiteleinstellungen Textfarbe @@ -275,8 +275,8 @@ Quellenfehler Remotefehler Renderfehler - Unerwarteter Player-Fehler - Download-Fehler, Speicherberechtigungen prüfen + Unerwarteter Playerfehler + Downloadfehler, Speicherberechtigungen prüfen Chromecast-Episode Chromecastmirror @@ -453,7 +453,7 @@ Trailer Link zum Stream Referent - Nächste + Weiter Videos in diesen Sprachen ansehen Vorherige Einrichtung überspringen @@ -497,4 +497,39 @@ Spuren Aktivieren der NSFW auf unterstützten Anbietern Anbieter + + Bewertung: %s + Beschreibung + Version + Status + Größe + Autoren + Unterstützt + Sprache + Zuerst muss die Erweiterung installiert werden + + HLS Playlist + + Bevorzugter Videoplayer + Interner Player + VLC + MPV + Web Video Cast + Browser + App nicht gefunden + Alle Sprachen + + Überspringen %s + Opening + Ending + Zusammenfassung + Vermischte Endings + Vermischte Openings + Abspann + Intro + + Verlauf löschen + Verlauf + Überspringen Button für Openings/Endings anzeigen + Zu viel Text. Kann nicht in der Zwischenablage gespeichert werden. From 456cd2e6e2e3cd15a74b5fcb37188365df3125bf Mon Sep 17 00:00:00 2001 From: Kylianalex <66625058+Kylianalex@users.noreply.github.com> Date: Fri, 11 Nov 2022 16:22:35 +0100 Subject: [PATCH 072/273] Updated French translation (#207) Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> --- app/src/main/res/values-fr/strings.xml | 68 +++++++++++++------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index c98173ce..e3673393 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -10,18 +10,18 @@ Aucune Donnée Plus d\'options Retour - Episode suivant + Épisode suivant Miniature Genres Partager - Ouvrir dans le naviguateur + Ouvrir dans le navigateur Passer le chargement Chargement… En visionnage - En pose + En pause Terminé Abandonné - A regarder + À regarder Aucun Lire Streamer le Torrent @@ -29,15 +29,15 @@ Sous-titres Réessayer la connection… Retour - Miniature de l\'Episode - Lire l\'Episode + Miniature de l\'Épisode + Lire l\'Épisode Télécharger Téléchargé Téléchargement Téléchargement en pause Téléchargement commencé - Echec du Téléchargement + Échec du Téléchargement Téléchargement Annulé Téléchargement Terminé Erreur lors du téléchargement des liens @@ -68,34 +68,34 @@ Couleur de fond Couleur de la fenètre Type de bordure - Elevation des sous-titres + Élévation des sous-titres Aperçu de l\'arrière-plan Police Rechercher en utilisant les fournisseurs Rechercher en utilisant les types %d Benenes données au dev Aucune Benenes donnée - Séléction automatique de la langue + Sélection automatique de la langue Télécharger les langues - Maintenir pour réinitialliser les valeurs par défault - Continer à regarder + Maintenir pour réinitialiser les valeurs par défaut + Continuer à regarder Supprimer Plus d\'info Un VPN peut être requit pour que ce fournisseur fonctionne - Ce fournisseur est un torrent, un VPN est recommendé + Ce fournisseur est un torrent, un VPN est recommandé Description Aucune description trouvée Aucune description trouvée Lecteur en mode Picture-in-Picture Continuer la lecture dans une fenêtre miniature en superposition sur d\'autres applis - Boutton de redimentionnement du lecteur + Bouton de redimensionnement du lecteur Supprimer les bordures noires Sous-titres Paramètres des sous-titres du lecteur Vitesse de lecture Ajouter l\'option de vitesse sur le lecteur Balayer pour avancer rapidement - Balayer vers la gauche ou la droite pour controler le temps du lecteur vidéo + Balayer vers la gauche ou la droite pour contrôler le temps du lecteur vidéo Balayer pour changer les paramètres Balayer sur le coté droit ou gauche pour changer le niveau de luminosité ou de volume Taper deux fois pour rechercher @@ -106,13 +106,13 @@ Rechercher Informations Recherche Avancée - Donne les résultats séparé par les fournisseur + Donne les résultats séparés par les fournisseurs N\'envoyer les données que lors d\'un crash N\'envoyer aucune données Afficher les mises-à-jour de l\'application Chercher des mises-à-jour automatiquement au démarage - Mettre à jour vers une version béta - Rechercher pour une mise à jour vers une version béta au lieu des version complètes seulement + Mettre à jour vers une version bêta + Rechercher pour une mise à jour vers une version bêta au lieu des version complètes seulement Github L\'application Light Novel par les mêmes devs Application d\'animés par les mêmes devs @@ -122,24 +122,24 @@ Language de l\'application Ce fournisseur ne supporte pas le Chromecast Aucun lien trouvé - Lien copié dans le presse-papiers + Lien copié dans le presse-papier Lecture de l\'episode Réinitialiser aux valeurs par défault - Désolé, l\'application à crashé. Un rapport de bug anonyme va être envoyé aux devloppeurs + Désolé, l\'application à crashé. Un rapport de bug anonyme va être envoyé aux développeurs Saison Pas de Saison - Episode - Episodes + Épisode + Épisodes S E Supprimer le Fichier Supprimer Pause Reprendre - Cela va supprimer définitivement %s\nÊtes vous sûr ? + Cela va supprimer définitivement %s\nÊtes-vous sûr ? En cours Terminé - Status + Statut Année Note Durée @@ -159,9 +159,9 @@ Erreur de la source Erreur distante Erreur d\'affichage - Erreur innatendue du lecteur + Erreur inattendue du lecteur Erreur du téléchargement, vérifier l\'autorisation du stockage - Episode Chromecast + Épisode Chromecast Miroir Chromecast Lecture dans l\'application Lecture dans %s @@ -207,18 +207,18 @@ Disposition de l\'application Disposition TV Language des fournisseurs - Médias préfères + Médias préférées Auto Acteurs: %s %d min Rechercher sur %s... - A re-regarder + À re-regarder Copier Coller Effacer Enregister Importer des polices en les plaçants dans %s - Les metadonnées ne sont pas fournies par le site, le chargement de la vidéo va échoué si elle n\'existe pas sur le site. + Les metadonnées ne sont pas fournies par le site, le chargement de la vidéo va échouer si elle n\'existe pas sur le site. Afficher les logs 🐈 Sous-titres Chromecast Paramètres des sous-titres Chromecast @@ -228,7 +228,7 @@ Restaurer les données sauvegardées Sauvegarder les données Fichier de sauvegarde chargé - Echec de la restauration des données depuis le fichier + Échec de la restauration des données depuis le fichier Restauration des données réussie Permission d\'accès au stockage manquante Erreur pendant la sauvegarde %s @@ -250,7 +250,7 @@ Causera des crashs aléatoire si mit trop haut. Ne le changez pas si vous avez un appareil avec peu de ram comme une télévision android ou un vieux téléphone. Causera des problèmes si mit trop haut et si vous avez un appareil avec peu de stockage comme une télévision android ou un vieux téléphone. Bouton aléatoire - Afficher le boutton aléatoire + Afficher le bouton aléatoire Disposition émulateur Position du titre du poster Mettre le titre sous le poster @@ -272,15 +272,15 @@ Contour Déprimé Ombre - Elevation + Élévation Synchroniser les sous-titres 1000ms Délai des sous-titres Utilisez si les sous-titres sont affichés %dms trop tôt Utilisez si les sous-titres sont affichés %dms trop tard Aucun délai de sous-titre - Le rapide rendard brun saute au-dessus du chien paresseux - Recommendé + Portez ce vieux whisky au juge blond qui fume + Recommandé %s chargé Charger depuis le fichier Fichier téléchargé @@ -289,7 +289,7 @@ Arrière plan Source Aléatoire - A venir ... + À venir ... Image de l\'affiche Connecté %s Définir le statut de visionage From cf22ada266191a3421dc8a213cdcd6f9fd3833b8 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 12 Nov 2022 20:07:08 +0100 Subject: [PATCH 073/273] Fix VoeExtractor --- .../cloudstream3/extractors/VoeExtractor.kt | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VoeExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VoeExtractor.kt index d2f3f832..ad3f0150 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VoeExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VoeExtractor.kt @@ -13,39 +13,42 @@ open class VoeExtractor : ExtractorApi() { override val requiresReferer = false private data class ResponseLinks( - @JsonProperty("hls") val url: String?, + @JsonProperty("hls") val hls: String?, + @JsonProperty("mp4") val mp4: String?, @JsonProperty("video_height") val label: Int? //val type: String // Mp4 ) override suspend fun getUrl(url: String, referer: String?): List { - val extractedLinksList: MutableList = mutableListOf() - val doc = app.get(url).text - if (doc.isNotBlank()) { - val start = "const sources =" - var src = doc.substring(doc.indexOf(start)) - src = src.substring(start.length, src.indexOf(";")) + val html = app.get(url).text + if (html.isNotBlank()) { + val src = html.substringAfter("const sources =").substringBefore(";") + // Remove last comma, it is not proper json otherwise .replace("0,", "0") - .trim() + // Make json use the proper quotes + .replace("'", "\"") + //Log.i(this.name, "Result => (src) ${src}") - parseJson(src)?.let { voelink -> - //Log.i(this.name, "Result => (voelink) ${voelink}") - val linkUrl = voelink.url - val linkLabel = voelink.label?.toString() ?: "" + parseJson(src)?.let { voeLink -> + //Log.i(this.name, "Result => (voeLink) ${voeLink}") + + // Always defaults to the hls link, but returns the mp4 if null + val linkUrl = voeLink.hls ?: voeLink.mp4 + val linkLabel = voeLink.label?.toString() ?: "" if (!linkUrl.isNullOrEmpty()) { - extractedLinksList.add( + return listOf( ExtractorLink( name = this.name, source = this.name, url = linkUrl, quality = getQualityFromName(linkLabel), referer = url, - isM3u8 = true + isM3u8 = voeLink.hls != null ) ) } } } - return extractedLinksList + return emptyList() } } \ No newline at end of file From 9a93b375f37bdec866b21965438712e312ef30f8 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 12 Nov 2022 22:29:22 +0100 Subject: [PATCH 074/273] Fixed: Lock when switching episodes Lock on RTL layouts Skipping to the next season --- .../cloudstream3/ui/player/FullScreenPlayer.kt | 2 +- .../cloudstream3/ui/result/ResultViewModel2.kt | 12 +++++++----- 2 files changed, 8 insertions(+), 6 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 0f9a6548..509c2187 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 @@ -612,6 +612,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { //player_media_route_button?.isClickable = !isGone player_go_back_holder?.isGone = isGone player_sources_btt?.isGone = isGone + player_skip_episode?.isGone = isGone } private fun updateLockUI() { @@ -1101,7 +1102,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { } protected fun uiReset() { - isLocked = false isShowing = false // if nothing has loaded these buttons should not be visible 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 0c26f69c..da900b0a 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 @@ -1582,7 +1582,6 @@ class ResultViewModel2 : ViewModel() { return } - val episodes = currentEpisodes[indexer] val ranges = currentRanges[indexer] if (ranges?.contains(range) != true) { @@ -1594,7 +1593,6 @@ class ResultViewModel2 : ViewModel() { } } - val size = episodes?.size val isMovie = currentResponse?.isMovie() == true currentIndex = indexer currentRange = range @@ -1604,6 +1602,7 @@ class ResultViewModel2 : ViewModel() { text to r } ?: emptyList()) + val size = currentEpisodes[indexer]?.size _episodesCountText.postValue( some( if (isMovie) null else @@ -1683,9 +1682,12 @@ class ResultViewModel2 : ViewModel() { generator = if (isMovie) { getMovie()?.let { RepoLinkGenerator(listOf(it), page = currentResponse) } } else { - episodes?.let { list -> - RepoLinkGenerator(list, page = currentResponse) - } + val episodes = currentEpisodes.filter { it.key.dubStatus == indexer.dubStatus } + .toList() + .sortedBy { it.first.season } + .flatMap { it.second } + + RepoLinkGenerator(episodes, page = currentResponse) } if (isMovie) { From 2b29e8078fe540a91ccaf6b9f91b9263a80acb6f Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 13 Nov 2022 01:40:49 +0100 Subject: [PATCH 075/273] Added intent to start searching --- app/src/main/AndroidManifest.xml | 11 +++++ .../lagradost/cloudstream3/MainActivity.kt | 42 +++++++++++++++---- .../syncproviders/AccountManager.kt | 3 ++ .../cloudstream3/ui/search/SearchFragment.kt | 19 ++++++++- .../main/res/navigation/mobile_navigation.xml | 7 +++- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 47676059..ae8479fe 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,17 @@ + + + + + + + + + + + diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index ff74d6cc..b999199f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -16,11 +16,9 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible import androidx.fragment.app.FragmentActivity -import androidx.navigation.NavController -import androidx.navigation.NavDestination +import androidx.navigation.* import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavGraph.Companion.findStartDestination -import androidx.navigation.NavOptions import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import androidx.preference.PreferenceManager @@ -44,6 +42,7 @@ import com.lagradost.cloudstream3.CommonActivity.onUserLeaveHint import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.updateLocale import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin @@ -52,9 +51,11 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2A import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO +import com.lagradost.cloudstream3.ui.search.SearchFragment import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings @@ -88,11 +89,9 @@ import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import okhttp3.ConnectionSpec -import okhttp3.OkHttpClient -import okhttp3.internal.applyConnectionSpec import java.io.File import java.net.URI +import java.net.URLDecoder import java.nio.charset.Charset import kotlin.reflect.KClass @@ -147,7 +146,7 @@ val VLC = ResultResume( val MPV = ResultResume( MPV_PACKAGE, //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive: - position = "position", + position = "position", duration = "duration", ) @@ -188,6 +187,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { companion object { const val TAG = "MAINACT" + /** + * Setting this will automatically enter the query in the search + * next time the search fragment is opened. + * This variable will clear itself after one use. Null does nothing. + * + * This is a very bad solution but I was unable to find a better one. + **/ + private var nextSearchQuery: String? = null + /** * Fires every time a new batch of plugins have been loaded, no guarantee about how often this is run and on which thread * */ @@ -206,6 +214,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { isWebview: Boolean ): Boolean = with(activity) { + // Invalid URIs can crash + fun safeURI(uri: String) = normalSafeApiCall { URI(uri) } + if (str != null && this != null) { if (str.startsWith("https://cs.repo")) { val realUrl = "https://" + str.substringAfter("?") @@ -241,10 +252,14 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { return true } } - } else if (URI(str).scheme == appStringRepo) { + } else if (safeURI(str)?.scheme == appStringRepo) { val url = str.replaceFirst(appStringRepo, "https") loadRepository(url) return true + } else if (safeURI(str)?.scheme == appStringSearch) { + nextSearchQuery = + URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8") + nav_view.selectedItemId = R.id.navigation_search } else if (!isWebview) { if (str.startsWith(DOWNLOAD_NAVIGATE_TO)) { this.navigate(R.id.navigation_downloads) @@ -619,6 +634,17 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController + + navController.addOnDestinationChangedListener { _: NavController, navDestination: NavDestination, bundle: Bundle? -> + // Intercept search and add a query + if (navDestination.matchDestination(R.id.navigation_search) && !nextSearchQuery.isNullOrBlank()) { + bundle?.apply { + this.putString(SearchFragment.SEARCH_QUERY, nextSearchQuery) + nextSearchQuery = null + } + } + } + //val navController = findNavController(R.id.nav_host_fragment) /*navOptions = NavOptions.Builder() diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index 825ff673..388e1774 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -43,6 +43,9 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { const val appString = "cloudstreamapp" const val appStringRepo = "cloudstreamrepo" + // Instantly start the search given a query + const val appStringSearch = "cloudstreamsearch" + val unixTime: Long get() = System.currentTimeMillis() / 1000L val unixTimeMs: Long 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 4da88af7..4e59e6a0 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 @@ -73,6 +73,14 @@ class SearchFragment : Fragment() { } } } + + const val SEARCH_QUERY = "search_query" + + fun newInstance(query: String): Bundle { + return Bundle().apply { + putString(SEARCH_QUERY, query) + } + } } private val searchViewModel: SearchViewModel by activityViewModels() @@ -132,7 +140,8 @@ class SearchFragment : Fragment() { val default = enumValues().sorted().filter { it != TvType.NSFW } .map { it.ordinal.toString() }.toSet() val preferredTypes = (PreferenceManager.getDefaultSharedPreferences(ctx) - .getStringSet(this.getString(R.string.prefer_media_type_key), default)?.ifEmpty { default } ?: default) + .getStringSet(this.getString(R.string.prefer_media_type_key), default) + ?.ifEmpty { default } ?: default) .mapNotNull { it.toIntOrNull() ?: return@mapNotNull null } val settings = ctx.getApiSettings() @@ -487,6 +496,14 @@ class SearchFragment : Fragment() { search_master_recycler?.adapter = masterAdapter search_master_recycler?.layoutManager = GridLayoutManager(context, 1) + // Automatically search the specified query, this allows the app search to launch from intent + arguments?.getString(SEARCH_QUERY)?.let { query -> + if (query.isBlank()) return@let + main_search?.setQuery(query, true) + // Clear the query as to not make it request the same query every time the page is opened + arguments?.putString(SEARCH_QUERY, null) + } + // SubtitlesFragment.push(activity) //searchViewModel.search("iron man") //(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro") diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 6ae2fa04..14d750a0 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -274,7 +274,12 @@ app:exitAnim="@anim/exit_anim" app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" - tools:layout="@layout/fragment_search" /> + tools:layout="@layout/fragment_search"> + + Date: Tue, 15 Nov 2022 16:32:31 +0100 Subject: [PATCH 076/273] Fix next episode button showing up erroneously --- .../com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 509c2187..c79cdd76 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 @@ -612,7 +612,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { //player_media_route_button?.isClickable = !isGone player_go_back_holder?.isGone = isGone player_sources_btt?.isGone = isGone - player_skip_episode?.isGone = isGone + player_skip_episode?.isClickable = !isGone } private fun updateLockUI() { From c9fe7c79dc7823bfa54adf29dc33c4cb7f50ed93 Mon Sep 17 00:00:00 2001 From: MXC48 <82712837+MXC48@users.noreply.github.com> Date: Fri, 18 Nov 2022 15:11:47 +0100 Subject: [PATCH 077/273] update the strings.xml in french (#211) * update the strings.xml in french * fix build error * Update and rename app/src/main/res/values-fr/strings.xml to application/src/principal/res/valeurs-fr/strings.xml Removal of "sort_copy" "sort_close" "sort_clear" "sort_save" in duplicate Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> --- .../src/principal/res/valeurs-fr}/strings.xml | 115 +++++++++++++++++- 1 file changed, 111 insertions(+), 4 deletions(-) rename {app/src/main/res/values-fr => application/src/principal/res/valeurs-fr}/strings.xml (73%) diff --git a/app/src/main/res/values-fr/strings.xml b/application/src/principal/res/valeurs-fr/strings.xml similarity index 73% rename from app/src/main/res/values-fr/strings.xml rename to application/src/principal/res/valeurs-fr/strings.xml index e3673393..6398d7ab 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/application/src/principal/res/valeurs-fr/strings.xml @@ -213,10 +213,6 @@ %d min Rechercher sur %s... À re-regarder - Copier - Coller - Effacer - Enregister Importer des polices en les plaçants dans %s Les metadonnées ne sont pas fournies par le site, le chargement de la vidéo va échouer si elle n\'existe pas sur le site. Afficher les logs 🐈 @@ -293,4 +289,115 @@ Image de l\'affiche Connecté %s Définir le statut de visionage + Annuler + Copier + Fermer + Vider + Enregistrer + + Vitesse du lecteur + + Paramètres des sous-titres + Couleur du texte + Couleur des contours + Couleur d'arrière-plan + Couleur de la fenêtre + Type de bordure + Elevation des sous-titres + Police + Taille de la police + + Recherche par fournisseur + Recherche par types + + %d Donner une banane aux devs + Aucun Bananes donné + + Sélection automatique de la langue + Télécharger les langues + Langue des sous-titres + Maintenir pour rétablir les valeurs par défaut + Importez des polices en les plaçant dans %s + Continuer à regarder + + Retirer + Plus d'informations + @string/home_play + + Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement + Ce fournisseur est un torrent, un VPN est recommandé + + Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n'existent pas sur le site. + + Description + Aucune trace trouvée + Aucune description trouvée + + Afficher le logcat 🐈 + + Picture-in-picture + Poursuite de la lecture dans un lecteur miniature au-dessus d'autres applications + Bouton de redimensionnement du lecteur + Supprimer les bordures noires + Sous-titres + Paramètres des sous-titres du lecteur + Sous-titres Chromecast + Paramètres des sous-titres Chromecast + + Mode Eigengravy + Ajout d'une option de vitesse dans le lecteur + Balayez pour chercher + Balayez vers la gauche ou la droite pour contrôler le temps dans le lecteur vidéo. + Balayez pour modifier les paramètres + Glissez sur le côté gauche ou droit pour modifier la luminosité ou le volume. + + Lecture automatique du prochain épisode + Démarrer l'épisode suivant lorsque l'épisode en cours se termine + + Double tape pour chercher + Double tape pour mettre en pause + Player seek amount + Tapez deux fois sur le côté droit ou gauche pour aller en avant ou en arrière. + + Tapez au milieu pour mettre en pause + Utiliser la luminosité du système + Utiliser la luminosité du système dans le lecteur d'applications au lieu du + sombre + + + Mise à jour de la progression de la veille + Synchronisation automatique de la progression de votre épisode en cours + + Restaurer des données à partir d'une sauvegarde + + Sauvegarde des données + Fichier de sauvegarde chargé + Échec de la restauration des données du fichier %s + Données stockées avec succès + Permissions de stockage manquantes, veuillez réessayer + Erreur de sauvegarde %s + + Recherche + Comptes + Mises à jour et sauvegarde + + Info + Recherche avancée + Vous donne les résultats de la recherche séparés par fournisseur + Envoi de données uniquement en cas d'accident + N'envoie aucune donnée + Afficher les épisodes spéciaux pour les animés + Montrer les bandes-annonces + Montrer les affiches de kitsu + Masquer la qualité vidéo sélectionnée dans les résultats de recherche + + Mises à jour automatiques des plugins + Afficher les mises à jour de l'application + Recherche automatique de nouvelles mises à jour au démarrage + Mettre à jour vers une version bêta + Recherche pour une mise à jour vers une version bêta au lieu des version complètes seulement + Github + Application Light Novel par les mêmes devs + Anime app by the same devs + Rejoignez le Discord From dbd91d788c773a58d4a78f4d2d700584720b252d Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Fri, 18 Nov 2022 18:11:25 +0100 Subject: [PATCH 078/273] ?? --- .../res/valeurs-fr => app/src/main/res/values-fr}/strings.xml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {application/src/principal/res/valeurs-fr => app/src/main/res/values-fr}/strings.xml (100%) diff --git a/application/src/principal/res/valeurs-fr/strings.xml b/app/src/main/res/values-fr/strings.xml similarity index 100% rename from application/src/principal/res/valeurs-fr/strings.xml rename to app/src/main/res/values-fr/strings.xml From 263f74fb9cf6cca91e327b0e7e7e12fc9f27e9e8 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Fri, 18 Nov 2022 20:24:24 +0100 Subject: [PATCH 079/273] Removed a million duplicates in values-fr to make it compile --- app/src/main/res/values-fr/strings.xml | 101 ++++--------------------- 1 file changed, 13 insertions(+), 88 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6398d7ab..f3c6a4c3 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -62,61 +62,7 @@ Appliquer Annuler Vitesse de lecture - Paramètres de sous-titres - Couleur du texte - Couleur de la bordure exterieur - Couleur de fond - Couleur de la fenètre - Type de bordure - Élévation des sous-titres Aperçu de l\'arrière-plan - Police - Rechercher en utilisant les fournisseurs - Rechercher en utilisant les types - %d Benenes données au dev - Aucune Benenes donnée - Sélection automatique de la langue - Télécharger les langues - Maintenir pour réinitialiser les valeurs par défaut - Continuer à regarder - Supprimer - Plus d\'info - Un VPN peut être requit pour que ce fournisseur fonctionne - Ce fournisseur est un torrent, un VPN est recommandé - Description - Aucune description trouvée - Aucune description trouvée - Lecteur en mode Picture-in-Picture - Continuer la lecture dans une fenêtre miniature en superposition sur d\'autres applis - Bouton de redimensionnement du lecteur - Supprimer les bordures noires - Sous-titres - Paramètres des sous-titres du lecteur - Vitesse de lecture - Ajouter l\'option de vitesse sur le lecteur - Balayer pour avancer rapidement - Balayer vers la gauche ou la droite pour contrôler le temps du lecteur vidéo - Balayer pour changer les paramètres - Balayer sur le coté droit ou gauche pour changer le niveau de luminosité ou de volume - Taper deux fois pour rechercher - - Taper deux fois pour mettre en pause - - Taper deux fois sur le coté droit ou gauche pour avancer ou reculer - Rechercher - Informations - Recherche Avancée - Donne les résultats séparés par les fournisseurs - N\'envoyer les données que lors d\'un crash - N\'envoyer aucune données - Afficher les mises-à-jour de l\'application - Chercher des mises-à-jour automatiquement au démarage - Mettre à jour vers une version bêta - Rechercher pour une mise à jour vers une version bêta au lieu des version complètes seulement - Github - L\'application Light Novel par les mêmes devs - Application d\'animés par les mêmes devs - Rejoindre le serveur Discord Donner une benene aux devs benenes données Language de l\'application @@ -192,16 +138,12 @@ Couleur principale Thème de l\'application Vitesse (%.2fx) - Utiliser la luminosité du système Général DNS avec HTTPS Afficher les animés en Anglais (Dub) / sous-titrés Disposition en mode téléphone %s Ep %d Note : %.1f - Taille de la police - Utiliser la luminosité du système dans le lecteur de l\'application au lieu d\'un écran noir - Afficher les épisodes spéciaux pour les animés Zoom Adapter à l\'écran Disposition de l\'application @@ -211,23 +153,8 @@ Auto Acteurs: %s %d min - Rechercher sur %s... + Rechercher sur %s… À re-regarder - Importer des polices en les plaçants dans %s - Les metadonnées ne sont pas fournies par le site, le chargement de la vidéo va échouer si elle n\'existe pas sur le site. - Afficher les logs 🐈 - Sous-titres Chromecast - Paramètres des sous-titres Chromecast - Taper au milieu pour mettre en pause - Mettre à jour la progression de visionnage - Synchroniser automatiquement votre progression de l\'épisode actuel - Restaurer les données sauvegardées - Sauvegarder les données - Fichier de sauvegarde chargé - Échec de la restauration des données depuis le fichier - Restauration des données réussie - Permission d\'accès au stockage manquante - Erreur pendant la sauvegarde %s Aucun épisode trouvé Documentaires OVA @@ -285,22 +212,20 @@ Arrière plan Source Aléatoire - À venir ... + À venir … Image de l\'affiche Connecté %s Définir le statut de visionage - Annuler Copier Fermer Vider Enregistrer - Vitesse du lecteur Paramètres des sous-titres Couleur du texte Couleur des contours - Couleur d'arrière-plan + Couleur d\'arrière-plan Couleur de la fenêtre Type de bordure Elevation des sous-titres @@ -321,13 +246,13 @@ Continuer à regarder Retirer - Plus d'informations + Plus d\'informations @string/home_play Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement Ce fournisseur est un torrent, un VPN est recommandé - Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n'existent pas sur le site. + Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site. Description Aucune trace trouvée @@ -336,7 +261,7 @@ Afficher le logcat 🐈 Picture-in-picture - Poursuite de la lecture dans un lecteur miniature au-dessus d'autres applications + Poursuite de la lecture dans un lecteur miniature au-dessus d\'autres applications Bouton de redimensionnement du lecteur Supprimer les bordures noires Sous-titres @@ -345,14 +270,14 @@ Paramètres des sous-titres Chromecast Mode Eigengravy - Ajout d'une option de vitesse dans le lecteur + Ajout d\'une option de vitesse dans le lecteur Balayez pour chercher Balayez vers la gauche ou la droite pour contrôler le temps dans le lecteur vidéo. Balayez pour modifier les paramètres Glissez sur le côté gauche ou droit pour modifier la luminosité ou le volume. Lecture automatique du prochain épisode - Démarrer l'épisode suivant lorsque l'épisode en cours se termine + Démarrer l\'épisode suivant lorsque l\'épisode en cours se termine Double tape pour chercher Double tape pour mettre en pause @@ -361,14 +286,14 @@ Tapez au milieu pour mettre en pause Utiliser la luminosité du système - Utiliser la luminosité du système dans le lecteur d'applications au lieu du + Utiliser la luminosité du système dans le lecteur d\'applications au lieu du sombre Mise à jour de la progression de la veille Synchronisation automatique de la progression de votre épisode en cours - Restaurer des données à partir d'une sauvegarde + Restaurer des données à partir d\'une sauvegarde Sauvegarde des données Fichier de sauvegarde chargé @@ -384,15 +309,15 @@ Info Recherche avancée Vous donne les résultats de la recherche séparés par fournisseur - Envoi de données uniquement en cas d'accident - N'envoie aucune donnée + Envoi de données uniquement en cas d\'accident + N\'envoie aucune donnée Afficher les épisodes spéciaux pour les animés Montrer les bandes-annonces Montrer les affiches de kitsu Masquer la qualité vidéo sélectionnée dans les résultats de recherche Mises à jour automatiques des plugins - Afficher les mises à jour de l'application + Afficher les mises à jour de l\'application Recherche automatique de nouvelles mises à jour au démarrage Mettre à jour vers une version bêta Recherche pour une mise à jour vers une version bêta au lieu des version complètes seulement From a8f3d18c2eca4e284a2c00b4d4558fc7760f23c9 Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Sat, 19 Nov 2022 18:53:34 +0800 Subject: [PATCH 080/273] [Feature] Get duration from string in format of '00 hr 00 min 00 sec', in any combination (#215) --- .../com/lagradost/cloudstream3/MainAPI.kt | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 5c9f3071..95d1faaa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -1153,6 +1153,27 @@ fun getDurationFromString(input: String?): Int? { return values[1].toIntOrNull() } } + Regex("(\\s\\d+\\shr)|(\\s\\d+\\shour)|(\\s\\d+\\smin)|(\\s\\d+\\ssec)").findAll(input).let { values -> + var seconds = 0 + values.forEach { + val time_text = it.value + if (time_text.isNotBlank()) { + val time = time_text.filter { s -> s.isDigit() }.trim().toInt() + val scale = time_text.filter { s -> !s.isDigit() }.trim() + //println("Scale: $scale") + val timeval = when (scale) { + "hr", "hour" -> time * 60 * 60 + "min" -> time * 60 + "sec" -> time + else -> 0 + } + seconds += timeval + } + } + if (seconds > 0) { + return seconds / 60 + } + } return null } From 9bca7a0780e69f1065ffb723de934c1f470cdf1c Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Mon, 21 Nov 2022 15:32:32 +0800 Subject: [PATCH 081/273] Fix duration regex, returning null on first checker (#218) --- app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 95d1faaa..e8ad476a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -1143,9 +1143,9 @@ fun getDurationFromString(input: String?): Int? { if (values.size == 3) { val hours = values[1].toIntOrNull() val minutes = values[2].toIntOrNull() - return if (minutes != null && hours != null) { - hours * 60 + minutes - } else null + if (minutes != null && hours != null) { + return hours * 60 + minutes + } } } Regex("([0-9]*)m").find(cleanInput)?.groupValues?.let { values -> From e21c8f8038fcf9c2f5e04388c6a2766ef8330658 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Wed, 23 Nov 2022 15:57:56 +0100 Subject: [PATCH 082/273] Fixed DdosGuardKiller, SSL on android 9 and some OpenSubtitles fixes --- app/build.gradle.kts | 3 ++- .../com/lagradost/cloudstream3/network/DdosGuardKiller.kt | 7 ++++--- .../com/lagradost/cloudstream3/network/RequestsHelper.kt | 4 ++++ .../syncproviders/providers/OpenSubtitlesApi.kt | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f72eb321..26e7d3a5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -191,7 +191,8 @@ dependencies { // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") implementation("com.github.Blatzar:NiceHttp:0.3.5") - + // To fix SSL fuckery on android 9 + implementation("org.conscrypt:conscrypt-android:2.2.1") // Util to skip the URI file fuckery 🙏 implementation("com.github.tachiyomiorg:unifile:17bec43") diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt b/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt index dca3ee00..b5783f78 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/DdosGuardKiller.kt @@ -2,7 +2,7 @@ package com.lagradost.cloudstream3.network import androidx.annotation.AnyThread import com.lagradost.cloudstream3.app -import com.lagradost.nicehttp.Requests.Companion.await +import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.cookies import kotlinx.coroutines.runBlocking import okhttp3.Interceptor @@ -41,7 +41,8 @@ class DdosGuardKiller(private val alwaysBypass: Boolean) : Interceptor { savedCookiesMap[request.url.host] // If no cookies are found fetch and save em. ?: (request.url.scheme + "://" + request.url.host + (ddosBypassPath ?: "")).let { - app.get(it, cacheTime = 0).cookies.also { cookies -> + // Somehow app.get fails + Requests().get(it).cookies.also { cookies -> savedCookiesMap[request.url.host] = cookies } } @@ -51,6 +52,6 @@ class DdosGuardKiller(private val alwaysBypass: Boolean) : Interceptor { request.newBuilder() .headers(headers) .build() - ).await() + ).execute() } } \ No newline at end of file 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 8bf1f91b..a1d84f6c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -4,15 +4,19 @@ 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.nicehttp.Requests import com.lagradost.nicehttp.ignoreAllSSLErrors import okhttp3.Cache import okhttp3.Headers import okhttp3.Headers.Companion.toHeaders import okhttp3.OkHttpClient +import org.conscrypt.Conscrypt import java.io.File +import java.security.Security fun Requests.initClient(context: Context): OkHttpClient { + normalSafeApiCall { Security.insertProviderAt(Conscrypt.newProvider(), 1) } val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val dns = settingsManager.getInt(context.getString(R.string.dns_pref), 0) baseClient = OkHttpClient.Builder() 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 bfa65f62..f22fdd8b 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 @@ -15,6 +15,8 @@ import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager import com.lagradost.cloudstream3.utils.AppUtils +import java.net.URLEncoder +import java.nio.charset.StandardCharsets class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi { override val idPrefix = "opensubtitles" @@ -175,7 +177,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi val searchQueryUrl = when (imdbId > 0) { //Use imdb_id to search if its valid true -> "$host/subtitles?imdb_id=$imdbId&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" - false -> "$host/subtitles?query=$queryText&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" + false -> "$host/subtitles?query=${URLEncoder.encode(queryText.lowercase(), StandardCharsets.UTF_8.toString())}&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" } val req = app.get( From 9be50eb28b861f90fc6a350fb9aea715a28390ff Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Sun, 27 Nov 2022 12:57:40 +0800 Subject: [PATCH 083/273] [Feature] Filter extension list automatically by preferred media language. --- .../ui/settings/extensions/PluginsFragment.kt | 16 ++++++++++++++++ app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/settings_providers.xml | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index bacd26c8..aa49c0e9 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -8,6 +8,9 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings +import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mvvm.observe @@ -45,6 +48,19 @@ class PluginsFragment : Fragment() { pluginViewModel.languages = listOf() pluginViewModel.search(null) + // Filter by language set on preferred media + activity?.let { + val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) + val filter = settingsManager.getBoolean(getString(R.string.pref_filter_ext_by_lang_key), false) + if (filter) { + val providerLangs = it.getApiProviderLangSettings().toList() + if (!providerLangs.contains(AllLanguagesName)) { + pluginViewModel.languages = providerLangs + //Log.i("DevDebug", "providerLang => ${pluginViewModel.languages.toJson()}") + } + } + } + val name = arguments?.getString(PLUGINS_BUNDLE_NAME) val url = arguments?.getString(PLUGINS_BUNDLE_URL) val isLocal = arguments?.getBoolean(PLUGINS_BUNDLE_LOCAL) == true diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9381372c..b4485c26 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,6 +58,7 @@ pref_filter_search_quality_key enable_nsfw_on_providers_key enable_skip_op_from_database + pref_filter_ext_by_lang_key %d %s | %s @@ -448,6 +449,7 @@ App Layout Preferred media Enable NSFW on supported providers + Filter Extension list by Preferred Language Subtitle encoding Providers Layout diff --git a/app/src/main/res/xml/settings_providers.xml b/app/src/main/res/xml/settings_providers.xml index a177865b..ffcd3511 100644 --- a/app/src/main/res/xml/settings_providers.xml +++ b/app/src/main/res/xml/settings_providers.xml @@ -21,4 +21,9 @@ android:icon="@drawable/ic_baseline_extension_24" android:summary="@string/apply_on_restart" app:defaultValue="false"/> + \ No newline at end of file From c513708d7432a1df0113796d413822cf5654b9f3 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 27 Nov 2022 10:37:36 +0100 Subject: [PATCH 084/273] smol tweaks to the previous commit --- .../ui/settings/extensions/PluginsFragment.kt | 13 ++++--------- app/src/main/res/values/strings.xml | 2 -- app/src/main/res/xml/settings_providers.xml | 5 ----- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index aa49c0e9..bd44a058 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -8,7 +8,6 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.AllLanguagesName import com.lagradost.cloudstream3.R @@ -50,14 +49,10 @@ class PluginsFragment : Fragment() { // Filter by language set on preferred media activity?.let { - val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) - val filter = settingsManager.getBoolean(getString(R.string.pref_filter_ext_by_lang_key), false) - if (filter) { - val providerLangs = it.getApiProviderLangSettings().toList() - if (!providerLangs.contains(AllLanguagesName)) { - pluginViewModel.languages = providerLangs - //Log.i("DevDebug", "providerLang => ${pluginViewModel.languages.toJson()}") - } + val providerLangs = it.getApiProviderLangSettings().toList() + if (!providerLangs.contains(AllLanguagesName)) { + pluginViewModel.languages = mutableListOf("none") + providerLangs + //Log.i("DevDebug", "providerLang => ${pluginViewModel.languages.toJson()}") } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4485c26..9381372c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -58,7 +58,6 @@ pref_filter_search_quality_key enable_nsfw_on_providers_key enable_skip_op_from_database - pref_filter_ext_by_lang_key %d %s | %s @@ -449,7 +448,6 @@ App Layout Preferred media Enable NSFW on supported providers - Filter Extension list by Preferred Language Subtitle encoding Providers Layout diff --git a/app/src/main/res/xml/settings_providers.xml b/app/src/main/res/xml/settings_providers.xml index ffcd3511..a177865b 100644 --- a/app/src/main/res/xml/settings_providers.xml +++ b/app/src/main/res/xml/settings_providers.xml @@ -21,9 +21,4 @@ android:icon="@drawable/ic_baseline_extension_24" android:summary="@string/apply_on_restart" app:defaultValue="false"/> - \ No newline at end of file From 58593ac8daabc829aab3618f18272c5518242ee7 Mon Sep 17 00:00:00 2001 From: jhih_yu Date: Wed, 30 Nov 2022 04:45:00 +0900 Subject: [PATCH 085/273] Add zh_TW (#202) * Add zh_TW --- README.md | 2 + .../lagradost/cloudstream3/CommonActivity.kt | 13 +- .../ui/settings/SettingsGeneral.kt | 7 +- app/src/main/res/values-zh-rTW/strings.xml | 591 ++++++++++++++++++ 4 files changed, 608 insertions(+), 5 deletions(-) create mode 100644 app/src/main/res/values-zh-rTW/strings.xml diff --git a/README.md b/README.md index 5e961c61..dcd4c5ed 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ ***The list of supported languages:*** * 🇱🇧 Arabic * 🇧🇬 Bulgarian +* 🇨🇳 Chinese Simplified +* 🇹🇼 Chinese Traditional * 🇭🇷 Croatian * 🇨🇿 Czech * 🇳🇱 Dutch diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 47a195d1..5f02661d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -108,9 +108,18 @@ object CommonActivity { } } + /** + * Not all languages can be fetched from locale with a code. + * This map allows sidestepping the default Locale(languageCode) + * when setting the app language. + **/ + val appLanguageExceptions = hashMapOf( + "zh_TW" to Locale.TRADITIONAL_CHINESE + ) + fun setLocale(context: Context?, languageCode: String?) { if (context == null || languageCode == null) return - val locale = Locale(languageCode) + val locale = appLanguageExceptions[languageCode] ?: Locale(languageCode) val resources: Resources = context.resources val config = resources.configuration Locale.setDefault(locale) @@ -421,4 +430,4 @@ object CommonActivity { } return null } -} \ No newline at end of file +} 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 8ea76cda..551a80ab 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 @@ -47,7 +47,7 @@ fun getCurrentLocale(context: Context): String { // Change locale settings in the app. // val dm = res.displayMetrics val conf = res.configuration - return conf?.locale?.language ?: "en" + return conf?.locale?.toString() ?: "en" } // idk, if you find a way of automating this it would be great @@ -75,7 +75,8 @@ val appLanguages = arrayListOf( Triple("\uD83C\uDDE7\uD83C\uDDF7", "Brazilian Portuguese", "bp"), Triple("", "Romanian", "ro"), Triple("", "Italian", "it"), - Triple("", "Chinese", "zh"), + Triple("", "Chinese Simplified", "zh"), + Triple("\uD83C\uDDF9\uD83C\uDDFC", "Chinese Traditional", "zh_TW"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"), Triple("", "Czech", "cs"), Triple("", "Croatian", "hr"), @@ -368,4 +369,4 @@ class SettingsGeneral : PreferenceFragmentCompat() { e.printStackTrace() } } -} \ 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 new file mode 100644 index 00000000..5b71a3f9 --- /dev/null +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,591 @@ + + + + %d %s | %s + %s • %s + %s / %s + %s %s + +%d + -%d + %d + %d + %.1f/10.0 + %d + %s 共 %d 集 + 演員:%s + 第 %d 集即將發佈於 + %dd %dh %dm + %dh %dm + %dm + + + 封面 + @string/result_poster_img_des + 劇集封面 + 主封面 + 隨機下一個 + @string/play_episode + 返回 + @string/home_change_provider_img_des + 更改片源 + 預覽背景 + + + 速度(%.2fx) + 評分:%.1f + 發現新版本!\n%s -> %s + 填充 + %d 分鐘 + + CloudStream + 使用 CloudStream 播放 + 主頁 + 搜尋 + 下載 + 設定 + + 搜尋… + 搜尋 %s… + + 無資料 + 更多選項 + 下一集 + @string/synopsis + 類型 + 分享 + 在瀏覽器中打開 + 跳過載入 + 載入中… + + 正在觀看 + 暫時擱置 + 觀看完畢 + 放棄觀看 + 計畫觀看 + + 重新觀看 + + 播放電影 + 播放直播 + 播放種子 + 來源 + 字幕 + 重試連接… + 返回 + 播放劇集 + + + 下載 + 已下載 + 下載中 + 下載暫停 + 下載開始 + 下載失敗 + 下載取消 + 下載完畢 + %s - %s + 播放 + + 載入連結錯誤 + 內部存儲 + + 配音 + 字幕 + + 刪除檔案 + 播放檔案 + 繼續下載 + 暫停下載 + + 禁用自動錯誤報告 + 更多資訊 + 隱藏 + 播放 + 資訊 + 篩選書籤 + 書籤 + 移除 + 設定觀看狀態 + 套用 + 取消 + 複製 + 關閉 + 清除 + 保存 + + 播放速度 + + 字幕設定 + 字體顏色 + 輪廓顏色 + 背景顏色 + 視窗顏色 + 邊緣類型 + 字幕高度 + 字體 + 字體大小 + + 按片源搜尋 + 按類型搜尋 + + 送開發者 %d 根香蕉 + 不送香蕉 + + 自動選擇語言 + 下載語言 + 字幕語言 + 按住重設為預設值 + 將字體導入到 %s + 繼續觀看 + + 移除 + 更多資訊 + @string/home_play + + 此片源可能需要 VPN 才能正常使用 + 此片源是種子,建議使用 VPN + + 站點不提供元數據,如果站點上不存在元數據,影片載入將失敗。 + + 簡介 + 未找到簡介 + 未找到簡介 + + 顯示 logcat 🐈 + + 字母畫面 + 在其他應用程式上的子母畫面中繼續播放 + 播放器調整大小按鈕 + 移除黑色邊框 + 字幕 + 播放器字幕設定 + Chromecast 字幕 + Chromecast 字幕設定 + + 播放速度 + 在播放器中添加播放速度選項 + 活動控制進度 + 左右滑動控制播放進度 + 滑動更改設定 + 上下滑動更改亮度或音量 + + 自動播放下一集 + 播放完畢後播放下一集 + + 輕按兩下以控制進度 + 輕按兩下以暫停 + 輕按兩下以控制進度時間 + 在右側或左側輕按兩次以向前或向後快轉 + + 輕按兩下中間以暫停 + 使用系統亮度 + 在應用程序播放器中使用系統亮度替代黑色遮罩 + + 更新觀看進度 + 自動同步當前劇集進度 + + 從備份中恢復資料 + + 備份資料 + 已載入備份資料 + 無法從 %s 檔案中還原資料 + 成功儲存資料 + 缺少儲存權限,請重試 + 備份 %s 錯誤 + + 搜尋 + 帳號 + 更新與備份 + + 資訊 + 進階搜尋 + 為您提供按片源分開的搜尋結果 + 僅在崩潰時傳送資料 + 不傳送資料 + 顯示動畫外傳 + 顯示預告片 + 顯示來自 Kitsu 的封面 + 在搜尋結果中隱藏選中的影片畫質 + + 自動更新外掛程式 + 顯示應用更新 + 啟動時自動搜尋更新 + 更新至預覽版 + 搜尋預覽版更新而不是僅搜尋正式版 + Github + 由相同開發者開發的輕小說應用程式 + 由相同開發者開發的動漫應用程式 + 加入 Discord + 送開發者一根香蕉 + 送香蕉 + + 應用程式語言 + + 此片源不支援 Chromecast + 未找到連結 + 連結已複製到剪貼簿 + 播放劇集 + 重設為預設值 + 很抱歉,應用崩潰了,將傳送一份匿名錯誤報告給開發者 + + + %s %d%s + 無季 + + + %d-%d + %d %s + S + E + 未找到劇集 + + 刪除文件 + 刪除 + @string/sort_cancel + 暫停 + 繼續 + -30 + +30 + 這將永遠刪除 %s\n你確定嗎? + 剩餘 %d 分鐘 + + + 連載中 + 已完結 + 狀態 + 年份 + 評分 + 時間 + 網站 + 簡介 + + 已加入佇列 + 無字幕 + 預設 + + 空閒 + 已使用 + 應用程式 + + + 電影 + 電視劇 + 卡通 + 動漫 + 種子 + 紀錄片 + 原創動畫錄影帶 + 亞洲劇 + 直播 + NSFW + 其他 + + + 電影 + 電視劇 + 卡通 + @string/anime + @string/ova + 種子 + 紀錄片 + 亞洲劇 + 直播 + NSFW + 其他 + + 來源錯誤 + 遠端錯誤 + 渲染器錯誤 + 意料之外的播放器錯誤 + 下載錯誤,請檢查儲存權限 + + Chromecast 劇集 + Chromecast 鏡像 + 在應用程式中播放 + 在 %s 中播放 + 在瀏覽器中播放 + 複製連結 + 自動下載 + 下載鏡像 + 重新載入連結 + 下載字幕 + + 畫質標籤 + 配音標籤 + 字幕標籤 + 標題 + show_hd_key + show_dub_key + show_sub_key + show_title_key + 封面內容 + + 未找到更新 + 检查更新 + + 鎖定 + 調整大小 + 來源 + 跳過片頭 + + 不再顯示 + 跳過此更新 + 更新 + 偏好播放畫質 + 影片播放器標題最大字數 + 影片播放器標題 + + 影片緩衝大小 + 影片緩衝長度 + 影片快取存儲 + 清除影片和圖片快取 + + 如果設定得太高會導致隨機崩潰。 如果您的記憶體不足(例如 Android TV 或舊手機),請不要更改 + 如果您將其設定得太高,可能會導致儲存空間不足的系統(例如 Android TV 設備)出現問題 + + DNS over HTTPS + 用於繞過網路服務供應商的封鎖 + + 複製片源 + 移除片源 + 添加具有不同URL的現有站點複製 + + 下載路徑 + + Nginx 伺服器連結 + + 顯示有配音/字幕的動漫 + + 適應螢幕 + 拉伸 + 縮放 + + 免責聲明 + 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. + + 通用 + 隨機按鈕 + 在主頁中顯示隨機按鈕 + 片源語言 + 應用佈局 + 偏好類型 + 在支援的片源中啟用 NSFW 內容 + 字幕編碼 + 片源 + 佈局 + + 自動 + 電視佈局 + 手機佈局 + 模擬器佈局 + + 主題色 + 應用程式主題 + 封面標題位置 + 將標題移到封面下方 + + + + anilist_key + mal_key + opensubtitles_key + nginx_key + 密碼 + 用戶名 + 電子郵件 + IP + 網站名稱 + 網站連結 + 語言代號 (zh_TW) + + + %s %s + 帳號 + 登出 + 登入 + 切換帳號 + 添加帳號 + 創建帳號 + 添加同步 + 已添加 %s + 同步 + 評分 + %d / 10 + /?? + /%d + 已驗證 %s + 驗證 %s 失敗 + + + + 普通 + 全部 + 最大 + 最小 + @string/none + 輪廓 + 凹陷 + 陰影 + 凸出 + 同步字幕 + 1000ms + 字幕延遲 + 如果字幕過早顯示 %dms ,請使用此選項 + 如果字幕過晚顯示 %dms ,請使用此選項 + 無字幕延遲 + + + The quick brown fox jumps over the lazy dog + + 推薦 + 已載入 %s + 從檔案載入 + 從網路載入 + 下載的檔案 + 主角 + 配角 + 群演 + + 來源 + 隨機 + + 即將到來… + + Cam + Cam + Cam + HQ + HD + TS + TC + BlueRay + WP + DVD + 4K + SD + UHD + HDR + SDR + Web + + 封面圖片 + 播放器 + 解析度與標題 + 標題 + 解析度 + 無效 ID + 無效資料 + 無效連結 + 錯誤 + 移除隱藏式字幕 + 移除字幕廣告 + 按偏好片源語言過濾 + 附加 + 預告片 + 播放連結 + 推薦 + 下一個 + 觀看這些語言的影片 + 上一個 + 跳過設定 + 更改應用程式的外觀以適應你的設備 + 崩潰報告 + 你想要看什麼 + 完成 + 擴充功能 + 添加資源庫 + 資源庫名稱 + 資源庫連結 + 外掛程式已載入 + 外掛程式已刪除 + 載入 %s 失敗 + 18+ + 開始下載 %d %s + 下載 %d %s 成功 + 全部 %s 已經下載 + 批次下載 + 外掛程式 + 外掛程式 + 這也將刪除所有資源庫外掛程式 + 刪除資源庫 + 下載你所需的片源 + 已下載:%d + 已禁用:%d + 未下載:%d + 已更新 %d 外掛程式 + CloudStream 預設沒有安裝任何片源。您需要從資源庫安裝站點。\n\n由於 Sky Uk Limited 的無腦 DMCA 刪除🤮,我們無法在應用程式中連結資源庫站點。\n\n加入我們的 Discord 獲得連結或自己在網路上搜尋 + 查看 + 公開列表 + 字幕全大寫 + + 從此資源庫下載所有外掛程式? + %s (禁用) + 軌道 + 音頻軌道 + 影片軌道 + 重新啟動時生效 + + 安全模式已啟用 + 發生了不可恢復的崩潰,我們已自動禁用所有外掛程式,因此您可以找到並刪除導致問題的應用程式。 + 查看崩潰資訊 + + 評分:%s + 簡介 + 版本 + 狀態 + 大小 + 作者 + 類型 + 語言 + 請先安裝外掛程式 + + HLS 播放清單 + + 偏好影片播放器 + 內部播放器 + VLC + MPV + 網路影片播放 + 瀏覽器 + 未找到應用 + 所有語言 + + 跳過 %s + 片頭 + 片尾 + 前情回顧 + 混合片尾 + 混合片頭 + 致謝名單 + 介紹 + + 清除歷史紀錄 + 歷史紀錄 + From 723c554bc86c3d6f45fa6d9abd05f0ef2874297b Mon Sep 17 00:00:00 2001 From: Jace <54625750+Jacekun@users.noreply.github.com> Date: Wed, 30 Nov 2022 03:46:31 +0800 Subject: [PATCH 086/273] [Feature] Automatically download plugin, based on language setting (#172) --- .../lagradost/cloudstream3/MainActivity.kt | 12 +- .../cloudstream3/plugins/PluginManager.kt | 107 ++++++++++++++++-- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/settings_updates.xml | 5 + 4 files changed, 115 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index b999199f..d7351dc7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -45,6 +45,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.plugins.PluginManager.loadAllOnlinePlugins import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis @@ -568,7 +569,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { ) { PluginManager.updateAllOnlinePluginsAndLoadThem(this@MainActivity) } else { - PluginManager.loadAllOnlinePlugins(this@MainActivity) + loadAllOnlinePlugins(this@MainActivity) + } + + //Automatically download not existing plugins + if (settingsManager.getBoolean( + getString(R.string.auto_download_plugins_key), + false + ) + ) { + PluginManager.downloadNotExistingPluginsAndLoad(this@MainActivity) } } 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 b9c775c0..f2dbb02f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -13,11 +13,13 @@ import androidx.core.app.NotificationManagerCompat import com.fasterxml.jackson.annotation.JsonProperty import com.google.gson.Gson import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.removePluginMapping 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.showToast +import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.mvvm.debugPrint import com.lagradost.cloudstream3.mvvm.logError @@ -26,6 +28,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.ui.settings.extensions.REPOSITORIES_KEY import com.lagradost.cloudstream3.ui.settings.extensions.RepositoryData import com.lagradost.cloudstream3.utils.Coroutines.main @@ -219,9 +223,7 @@ object PluginManager { fun updateAllOnlinePluginsAndLoadThem(activity: Activity) { // Load all plugins as fast as possible! loadAllOnlinePlugins(activity) - - afterPluginsLoadedEvent.invoke(true) - + afterPluginsLoadedEvent.invoke(true) val urls = (getKey>(REPOSITORIES_KEY) ?: emptyArray()) + PREBUILT_REPOSITORIES @@ -265,16 +267,98 @@ object PluginManager { } main { - createNotification(activity, updatedPlugins) + val uitext = txt(R.string.plugins_updated, updatedPlugins.size) + createNotification(activity, uitext, updatedPlugins) } - // ioSafe { + // ioSafe { afterPluginsLoadedEvent.invoke(true) - // } + // } Log.i(TAG, "Plugin update done!") } + /** + * Automatically download plugins not yet existing on local + * 1. Gets all online data from online plugins repo + * 2. Fetch all not downloaded plugins + * 3. Download them and reload plugins + **/ + fun downloadNotExistingPluginsAndLoad(activity: Activity) { + val newDownloadPlugins = mutableListOf() + val urls = (getKey>(REPOSITORIES_KEY) + ?: emptyArray()) + PREBUILT_REPOSITORIES + val onlinePlugins = urls.toList().apmap { + getRepoPlugins(it.url)?.toList() ?: emptyList() + }.flatten().distinctBy { it.second.url } + + val providerLang = activity.getApiProviderLangSettings() + //Log.i(TAG, "providerLang => ${providerLang.toJson()}") + + // Iterate online repos and returns not downloaded plugins + val notDownloadedPlugins = onlinePlugins.mapNotNull { onlineData -> + val sitePlugin = onlineData.second + //Don't include empty urls + if (sitePlugin.url.isBlank()) { return@mapNotNull null } + if (sitePlugin.repositoryUrl.isNullOrBlank()) { return@mapNotNull null } + + //Omit already existing plugins + if (getPluginPath(activity, sitePlugin.internalName, onlineData.first).exists()) { + Log.i(TAG, "Skip > ${sitePlugin.internalName}") + return@mapNotNull null + } + + //Omit lang not selected on language setting + val lang = sitePlugin.language ?: return@mapNotNull null + //If set to 'universal', don't skip any language + if (!providerLang.contains(AllLanguagesName) && !providerLang.contains(lang)) { + return@mapNotNull null + } + //Log.i(TAG, "sitePlugin lang => $lang") + + //Omit NSFW, if disabled + sitePlugin.tvTypes?.let { tvtypes -> + if (!settingsForProvider.enableAdult) { + if (tvtypes.contains(TvType.NSFW.name)) { + return@mapNotNull null + } + } + } + val savedData = PluginData( + url = sitePlugin.url, + internalName = sitePlugin.internalName, + isOnline = true, + filePath = "", + version = sitePlugin.version + ) + OnlinePluginData(savedData, onlineData) + } + //Log.i(TAG, "notDownloadedPlugins => ${notDownloadedPlugins.toJson()}") + + notDownloadedPlugins.apmap { pluginData -> + downloadAndLoadPlugin( + activity, + pluginData.onlineData.second.url, + pluginData.savedData.internalName, + pluginData.onlineData.first + ).let { success -> + if (success) + newDownloadPlugins.add(pluginData.onlineData.second.name) + } + } + + main { + val uitext = txt(R.string.plugins_downloaded, newDownloadPlugins.size) + createNotification(activity, uitext, newDownloadPlugins) + } + + // ioSafe { + afterPluginsLoadedEvent.invoke(true) + // } + + Log.i(TAG, "Plugin download done!") + } + /** * Use updateAllOnlinePluginsAndLoadThem * */ @@ -527,12 +611,14 @@ object PluginManager { private fun createNotification( context: Context, - extensionNames: List + uitext: UiText, + extensions: List ): Notification? { try { - if (extensionNames.isEmpty()) return null - val content = extensionNames.joinToString(", ") + if (extensions.isEmpty()) return null + + val content = extensions.joinToString(", ") // main { // DON'T WANT TO SLOW IT DOWN val builder = NotificationCompat.Builder(context, EXTENSIONS_CHANNEL_ID) .setAutoCancel(false) @@ -541,7 +627,8 @@ object PluginManager { .setSilent(true) .setPriority(NotificationCompat.PRIORITY_LOW) .setColor(context.colorFromAttribute(R.attr.colorPrimary)) - .setContentTitle(context.getString(R.string.plugins_updated, extensionNames.size)) + .setContentTitle(uitext.asString(context)) + //.setContentTitle(context.getString(title, extensionNames.size)) .setSmallIcon(R.drawable.ic_baseline_extension_24) .setStyle( NotificationCompat.BigTextStyle() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9381372c..db042b95 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,6 +6,7 @@ search_type_list auto_update auto_update_plugins + auto_download_plugins_key skip_update_key prerelease_update manual_check_update @@ -269,6 +270,7 @@ Hide selected video quality on Search results Automatic plugin updates + Automatically download plugins Show app updates Automatically search for new updates on start Update to prereleases diff --git a/app/src/main/res/xml/settings_updates.xml b/app/src/main/res/xml/settings_updates.xml index eaceb785..3a17f393 100644 --- a/app/src/main/res/xml/settings_updates.xml +++ b/app/src/main/res/xml/settings_updates.xml @@ -33,6 +33,11 @@ android:icon="@drawable/ic_baseline_extension_24" android:key="@string/auto_update_plugins_key" android:title="@string/automatic_plugin_updates" /> + Date: Wed, 30 Nov 2022 21:23:19 +0100 Subject: [PATCH 087/273] Fixed MPV return intent --- .../lagradost/cloudstream3/CommonActivity.kt | 4 +-- .../lagradost/cloudstream3/MainActivity.kt | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 5f02661d..ef55eff0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -155,8 +155,8 @@ object CommonActivity { val resultCode = result.resultCode val data = result.data if (resultCode == AppCompatActivity.RESULT_OK && data != null && resumeApp.position != null && resumeApp.duration != null) { - val pos = data.getLongExtra(resumeApp.position, -1L) - val dur = data.getLongExtra(resumeApp.duration, -1L) + 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) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index d7351dc7..c038d23a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -115,13 +115,15 @@ val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlay val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity") //TODO REFACTOR AF -data class ResultResume( +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) @@ -135,21 +137,45 @@ data class ResultResume( callback.invoke(intent) launcher?.launch(intent) } + + open fun getPosition(intent: Intent?): Long { + return defaultTime + } + + open fun getDuration(intent: Intent?): Long { + return defaultTime + } } -val VLC = ResultResume( +val VLC = object : ResultResume( VLC_PACKAGE, "org.videolan.vlc.player.result", "extra_position", "extra_duration", -) +) { + override fun getPosition(intent: Intent?): Long { + return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime + } -val MPV = ResultResume( + 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 WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE) From e21574774911a4228116ad2203e54a1c9196a77f Mon Sep 17 00:00:00 2001 From: Sdarfeesh <50188628+Sdarfeesh@users.noreply.github.com> Date: Fri, 2 Dec 2022 05:07:47 +0800 Subject: [PATCH 088/273] Update Simplified Chinese Translation (#229) --- app/src/main/res/values-zh/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 19d9bd43..d609b9af 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -209,6 +209,7 @@ 在搜索结果中隐藏选中视频画质 自动更新插件 + 自动下载插件 显示应用更新 启动时自动搜索更新 更新至预览版 From b79e2d768f4bdb39aaa3406b499b3edb5bfec8e2 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 3 Dec 2022 02:42:16 +0100 Subject: [PATCH 089/273] Fixed subtitle elevation again --- .../ui/player/NonFinalTextRenderer.java | 699 +++++++++--------- 1 file changed, 366 insertions(+), 333 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.java b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.java index 8602ce25..ce39a841 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.java +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/NonFinalTextRenderer.java @@ -15,6 +15,8 @@ */ package com.lagradost.cloudstream3.ui.player; +import static com.google.android.exoplayer2.text.Cue.DIMEN_UNSET; +import static com.google.android.exoplayer2.text.Cue.LINE_TYPE_NUMBER; import static com.google.android.exoplayer2.util.Assertions.checkNotNull; import static com.google.android.exoplayer2.util.Assertions.checkState; import static java.lang.annotation.ElementType.TYPE_USE; @@ -23,8 +25,10 @@ import android.os.Handler; import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; + import com.google.android.exoplayer2.BaseRenderer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -43,13 +47,17 @@ import com.google.android.exoplayer2.text.TextOutput; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.Util; + import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; // DO NOT CONVERT TO KOTLIN AUTOMATICALLY, IT FUCKS UP AND DOES NOT DISPLAY SUBS FOR SOME REASON +// IF YOU CHANGE THE CODE MAKE SURE YOU GET THE CUES CORRECT! + /** * A renderer for text. * @@ -59,365 +67,390 @@ import java.util.List; */ public class NonFinalTextRenderer extends BaseRenderer implements Callback { - private static final String TAG = "TextRenderer"; + private static final String TAG = "TextRenderer"; - /** - * @param trackType The track type that the renderer handles. One of the {@link C} {@code - * TRACK_TYPE_*} constants. - * @param outputHandler - */ - public NonFinalTextRenderer(int trackType, @Nullable Handler outputHandler) { - super(trackType); - this.outputHandler = outputHandler; - } - - @Documented - @Retention(RetentionPolicy.SOURCE) - @Target(TYPE_USE) - @IntDef({ - REPLACEMENT_STATE_NONE, - REPLACEMENT_STATE_SIGNAL_END_OF_STREAM, - REPLACEMENT_STATE_WAIT_END_OF_STREAM - }) - private @interface ReplacementState {} - /** The decoder does not need to be replaced. */ - private static final int REPLACEMENT_STATE_NONE = 0; - /** - * The decoder needs to be replaced, but we haven't yet signaled an end of stream to the existing - * decoder. We need to do so in order to ensure that it outputs any remaining buffers before we - * release it. - */ - private static final int REPLACEMENT_STATE_SIGNAL_END_OF_STREAM = 1; - /** - * The decoder needs to be replaced, and we've signaled an end of stream to the existing decoder. - * We're waiting for the decoder to output an end of stream signal to indicate that it has output - * any remaining buffers before we release it. - */ - private static final int REPLACEMENT_STATE_WAIT_END_OF_STREAM = 2; - - private static final int MSG_UPDATE_OUTPUT = 0; - - @Nullable private final Handler outputHandler; - private TextOutput output = null; - private SubtitleDecoderFactory decoderFactory = null; - private FormatHolder formatHolder = null; - - private boolean inputStreamEnded; - private boolean outputStreamEnded; - private boolean waitingForKeyFrame; - private @ReplacementState int decoderReplacementState; - @Nullable private Format streamFormat; - @Nullable private SubtitleDecoder decoder; - @Nullable private SubtitleInputBuffer nextInputBuffer; - @Nullable private SubtitleOutputBuffer subtitle; - @Nullable private SubtitleOutputBuffer nextSubtitle; - private int nextSubtitleEventIndex; - private long finalStreamEndPositionUs; - - /** - * @param output The output. - * @param outputLooper The looper associated with the thread on which the output should be called. - * If the output makes use of standard Android UI components, then this should normally be the - * looper associated with the application's main thread, which can be obtained using {@link - * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called - * directly on the player's internal rendering thread. - */ - public NonFinalTextRenderer(TextOutput output, @Nullable Looper outputLooper) { - this(output, outputLooper, SubtitleDecoderFactory.DEFAULT); - } - - /** - * @param output The output. - * @param outputLooper The looper associated with the thread on which the output should be called. - * If the output makes use of standard Android UI components, then this should normally be the - * looper associated with the application's main thread, which can be obtained using {@link - * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called - * directly on the player's internal rendering thread. - * @param decoderFactory A factory from which to obtain {@link SubtitleDecoder} instances. - */ - public NonFinalTextRenderer( - TextOutput output, @Nullable Looper outputLooper, SubtitleDecoderFactory decoderFactory) { - super(C.TRACK_TYPE_TEXT); - this.output = checkNotNull(output); - this.outputHandler = - outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this); - this.decoderFactory = decoderFactory; - formatHolder = new FormatHolder(); - finalStreamEndPositionUs = C.TIME_UNSET; - } - - @Override - public String getName() { - return TAG; - } - - @Override - public @Capabilities int supportsFormat(Format format) { - if (decoderFactory.supportsFormat(format)) { - return RendererCapabilities.create( - format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); - } else if (MimeTypes.isText(format.sampleMimeType)) { - return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE); - } else { - return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); - } - } - - /** - * Sets the position at which to stop rendering the current stream. - * - *

Must be called after {@link #setCurrentStreamFinal()}. - * - * @param streamEndPositionUs The position to stop rendering at or {@link C#LENGTH_UNSET} to - * render until the end of the current stream. - */ - // TODO(internal b/181312195): Remove this when it's no longer needed once subtitles are decoded - // on the loading side of SampleQueue. - public void setFinalStreamEndPositionUs(long streamEndPositionUs) { - checkState(isCurrentStreamFinal()); - this.finalStreamEndPositionUs = streamEndPositionUs; - } - - @Override - protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) { - streamFormat = formats[0]; - if (decoder != null) { - decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM; - } else { - initDecoder(); - } - } - - @Override - protected void onPositionReset(long positionUs, boolean joining) { - clearOutput(); - inputStreamEnded = false; - outputStreamEnded = false; - finalStreamEndPositionUs = C.TIME_UNSET; - if (decoderReplacementState != REPLACEMENT_STATE_NONE) { - replaceDecoder(); - } else { - releaseBuffers(); - checkNotNull(decoder).flush(); - } - } - - @Override - public void render(long positionUs, long elapsedRealtimeUs) { - if (isCurrentStreamFinal() - && finalStreamEndPositionUs != C.TIME_UNSET - && positionUs >= finalStreamEndPositionUs) { - releaseBuffers(); - outputStreamEnded = true; + /** + * @param trackType The track type that the renderer handles. One of the {@link C} {@code + * TRACK_TYPE_*} constants. + * @param outputHandler + */ + public NonFinalTextRenderer(int trackType, @Nullable Handler outputHandler) { + super(trackType); + this.outputHandler = outputHandler; } - if (outputStreamEnded) { - return; + @Documented + @Retention(RetentionPolicy.SOURCE) + @Target(TYPE_USE) + @IntDef({ + REPLACEMENT_STATE_NONE, + REPLACEMENT_STATE_SIGNAL_END_OF_STREAM, + REPLACEMENT_STATE_WAIT_END_OF_STREAM + }) + private @interface ReplacementState { } - if (nextSubtitle == null) { - checkNotNull(decoder).setPositionUs(positionUs); - try { - nextSubtitle = checkNotNull(decoder).dequeueOutputBuffer(); - } catch (SubtitleDecoderException e) { - handleDecoderError(e); - return; - } + /** + * The decoder does not need to be replaced. + */ + private static final int REPLACEMENT_STATE_NONE = 0; + /** + * The decoder needs to be replaced, but we haven't yet signaled an end of stream to the existing + * decoder. We need to do so in order to ensure that it outputs any remaining buffers before we + * release it. + */ + private static final int REPLACEMENT_STATE_SIGNAL_END_OF_STREAM = 1; + /** + * The decoder needs to be replaced, and we've signaled an end of stream to the existing decoder. + * We're waiting for the decoder to output an end of stream signal to indicate that it has output + * any remaining buffers before we release it. + */ + private static final int REPLACEMENT_STATE_WAIT_END_OF_STREAM = 2; + + private static final int MSG_UPDATE_OUTPUT = 0; + + @Nullable + private final Handler outputHandler; + private TextOutput output = null; + private SubtitleDecoderFactory decoderFactory = null; + private FormatHolder formatHolder = null; + + private boolean inputStreamEnded; + private boolean outputStreamEnded; + private boolean waitingForKeyFrame; + private @ReplacementState int decoderReplacementState; + @Nullable + private Format streamFormat; + @Nullable + private SubtitleDecoder decoder; + @Nullable + private SubtitleInputBuffer nextInputBuffer; + @Nullable + private SubtitleOutputBuffer subtitle; + @Nullable + private SubtitleOutputBuffer nextSubtitle; + private int nextSubtitleEventIndex; + private long finalStreamEndPositionUs; + + /** + * @param output The output. + * @param outputLooper The looper associated with the thread on which the output should be called. + * If the output makes use of standard Android UI components, then this should normally be the + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. + */ + public NonFinalTextRenderer(TextOutput output, @Nullable Looper outputLooper) { + this(output, outputLooper, SubtitleDecoderFactory.DEFAULT); } - if (getState() != STATE_STARTED) { - return; + /** + * @param output The output. + * @param outputLooper The looper associated with the thread on which the output should be called. + * If the output makes use of standard Android UI components, then this should normally be the + * looper associated with the application's main thread, which can be obtained using {@link + * android.app.Activity#getMainLooper()}. Null may be passed if the output should be called + * directly on the player's internal rendering thread. + * @param decoderFactory A factory from which to obtain {@link SubtitleDecoder} instances. + */ + public NonFinalTextRenderer( + TextOutput output, @Nullable Looper outputLooper, SubtitleDecoderFactory decoderFactory) { + super(C.TRACK_TYPE_TEXT); + this.output = checkNotNull(output); + this.outputHandler = + outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this); + this.decoderFactory = decoderFactory; + formatHolder = new FormatHolder(); + finalStreamEndPositionUs = C.TIME_UNSET; } - boolean textRendererNeedsUpdate = false; - if (subtitle != null) { - // We're iterating through the events in a subtitle. Set textRendererNeedsUpdate if we - // advance to the next event. - long subtitleNextEventTimeUs = getNextEventTime(); - while (subtitleNextEventTimeUs <= positionUs) { - nextSubtitleEventIndex++; - subtitleNextEventTimeUs = getNextEventTime(); - textRendererNeedsUpdate = true; - } + @Override + public String getName() { + return TAG; } - if (nextSubtitle != null) { - SubtitleOutputBuffer nextSubtitle = this.nextSubtitle; - if (nextSubtitle.isEndOfStream()) { - if (!textRendererNeedsUpdate && getNextEventTime() == Long.MAX_VALUE) { - if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { + + @Override + public @Capabilities int supportsFormat(Format format) { + if (decoderFactory.supportsFormat(format)) { + return RendererCapabilities.create( + format.cryptoType == C.CRYPTO_TYPE_NONE ? C.FORMAT_HANDLED : C.FORMAT_UNSUPPORTED_DRM); + } else if (MimeTypes.isText(format.sampleMimeType)) { + return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_SUBTYPE); + } else { + return RendererCapabilities.create(C.FORMAT_UNSUPPORTED_TYPE); + } + } + + /** + * Sets the position at which to stop rendering the current stream. + * + *

Must be called after {@link #setCurrentStreamFinal()}. + * + * @param streamEndPositionUs The position to stop rendering at or {@link C#LENGTH_UNSET} to + * render until the end of the current stream. + */ + // TODO(internal b/181312195): Remove this when it's no longer needed once subtitles are decoded + // on the loading side of SampleQueue. + public void setFinalStreamEndPositionUs(long streamEndPositionUs) { + checkState(isCurrentStreamFinal()); + this.finalStreamEndPositionUs = streamEndPositionUs; + } + + @Override + protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) { + streamFormat = formats[0]; + if (decoder != null) { + decoderReplacementState = REPLACEMENT_STATE_SIGNAL_END_OF_STREAM; + } else { + initDecoder(); + } + } + + @Override + protected void onPositionReset(long positionUs, boolean joining) { + clearOutput(); + inputStreamEnded = false; + outputStreamEnded = false; + finalStreamEndPositionUs = C.TIME_UNSET; + if (decoderReplacementState != REPLACEMENT_STATE_NONE) { replaceDecoder(); - } else { + } else { + releaseBuffers(); + checkNotNull(decoder).flush(); + } + } + + @Override + public void render(long positionUs, long elapsedRealtimeUs) { + if (isCurrentStreamFinal() + && finalStreamEndPositionUs != C.TIME_UNSET + && positionUs >= finalStreamEndPositionUs) { releaseBuffers(); outputStreamEnded = true; - } } - } else if (nextSubtitle.timeUs <= positionUs) { - // Advance to the next subtitle. Sync the next event index and trigger an update. - if (subtitle != null) { - subtitle.release(); - } - nextSubtitleEventIndex = nextSubtitle.getNextEventTimeIndex(positionUs); - subtitle = nextSubtitle; - this.nextSubtitle = null; - textRendererNeedsUpdate = true; - } - } - if (textRendererNeedsUpdate) { - // If textRendererNeedsUpdate then subtitle must be non-null. - checkNotNull(subtitle); - // textRendererNeedsUpdate is set and we're playing. Update the renderer. - updateOutput(subtitle.getCues(positionUs)); - } - - if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { - return; - } - - try { - while (!inputStreamEnded) { - @Nullable SubtitleInputBuffer nextInputBuffer = this.nextInputBuffer; - if (nextInputBuffer == null) { - nextInputBuffer = checkNotNull(decoder).dequeueInputBuffer(); - if (nextInputBuffer == null) { + if (outputStreamEnded) { return; - } - this.nextInputBuffer = nextInputBuffer; } - if (decoderReplacementState == REPLACEMENT_STATE_SIGNAL_END_OF_STREAM) { - nextInputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); - checkNotNull(decoder).queueInputBuffer(nextInputBuffer); - this.nextInputBuffer = null; - decoderReplacementState = REPLACEMENT_STATE_WAIT_END_OF_STREAM; - return; - } - // Try and read the next subtitle from the source. - @ReadDataResult int result = readSource(formatHolder, nextInputBuffer, /* readFlags= */ 0); - if (result == C.RESULT_BUFFER_READ) { - if (nextInputBuffer.isEndOfStream()) { - inputStreamEnded = true; - waitingForKeyFrame = false; - } else { - @Nullable Format format = formatHolder.format; - if (format == null) { - // We haven't received a format yet. - return; + + if (nextSubtitle == null) { + checkNotNull(decoder).setPositionUs(positionUs); + try { + nextSubtitle = checkNotNull(decoder).dequeueOutputBuffer(); + } catch (SubtitleDecoderException e) { + handleDecoderError(e); + return; } - nextInputBuffer.subsampleOffsetUs = format.subsampleOffsetUs; - nextInputBuffer.flip(); - waitingForKeyFrame &= !nextInputBuffer.isKeyFrame(); - } - if (!waitingForKeyFrame) { - checkNotNull(decoder).queueInputBuffer(nextInputBuffer); - this.nextInputBuffer = null; - } - } else if (result == C.RESULT_NOTHING_READ) { - return; } - } - } catch (SubtitleDecoderException e) { - handleDecoderError(e); + + if (getState() != STATE_STARTED) { + return; + } + + boolean textRendererNeedsUpdate = false; + if (subtitle != null) { + // We're iterating through the events in a subtitle. Set textRendererNeedsUpdate if we + // advance to the next event. + long subtitleNextEventTimeUs = getNextEventTime(); + while (subtitleNextEventTimeUs <= positionUs) { + nextSubtitleEventIndex++; + subtitleNextEventTimeUs = getNextEventTime(); + textRendererNeedsUpdate = true; + } + } + if (nextSubtitle != null) { + SubtitleOutputBuffer nextSubtitle = this.nextSubtitle; + if (nextSubtitle.isEndOfStream()) { + if (!textRendererNeedsUpdate && getNextEventTime() == Long.MAX_VALUE) { + if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { + replaceDecoder(); + } else { + releaseBuffers(); + outputStreamEnded = true; + } + } + } else if (nextSubtitle.timeUs <= positionUs) { + // Advance to the next subtitle. Sync the next event index and trigger an update. + if (subtitle != null) { + subtitle.release(); + } + nextSubtitleEventIndex = nextSubtitle.getNextEventTimeIndex(positionUs); + subtitle = nextSubtitle; + this.nextSubtitle = null; + textRendererNeedsUpdate = true; + } + } + + if (textRendererNeedsUpdate) { + // If textRendererNeedsUpdate then subtitle must be non-null. + checkNotNull(subtitle); + // textRendererNeedsUpdate is set and we're playing. Update the renderer. + updateOutput(subtitle.getCues(positionUs)); + } + + if (decoderReplacementState == REPLACEMENT_STATE_WAIT_END_OF_STREAM) { + return; + } + + try { + while (!inputStreamEnded) { + @Nullable SubtitleInputBuffer nextInputBuffer = this.nextInputBuffer; + if (nextInputBuffer == null) { + nextInputBuffer = checkNotNull(decoder).dequeueInputBuffer(); + if (nextInputBuffer == null) { + return; + } + this.nextInputBuffer = nextInputBuffer; + } + if (decoderReplacementState == REPLACEMENT_STATE_SIGNAL_END_OF_STREAM) { + nextInputBuffer.setFlags(C.BUFFER_FLAG_END_OF_STREAM); + checkNotNull(decoder).queueInputBuffer(nextInputBuffer); + this.nextInputBuffer = null; + decoderReplacementState = REPLACEMENT_STATE_WAIT_END_OF_STREAM; + return; + } + // Try and read the next subtitle from the source. + @ReadDataResult int result = readSource(formatHolder, nextInputBuffer, /* readFlags= */ 0); + if (result == C.RESULT_BUFFER_READ) { + if (nextInputBuffer.isEndOfStream()) { + inputStreamEnded = true; + waitingForKeyFrame = false; + } else { + @Nullable Format format = formatHolder.format; + if (format == null) { + // We haven't received a format yet. + return; + } + nextInputBuffer.subsampleOffsetUs = format.subsampleOffsetUs; + nextInputBuffer.flip(); + waitingForKeyFrame &= !nextInputBuffer.isKeyFrame(); + } + if (!waitingForKeyFrame) { + checkNotNull(decoder).queueInputBuffer(nextInputBuffer); + this.nextInputBuffer = null; + } + } else if (result == C.RESULT_NOTHING_READ) { + return; + } + } + } catch (SubtitleDecoderException e) { + handleDecoderError(e); + } } - } - @Override - protected void onDisabled() { - streamFormat = null; - finalStreamEndPositionUs = C.TIME_UNSET; - clearOutput(); - releaseDecoder(); - } - - @Override - public boolean isEnded() { - return outputStreamEnded; - } - - @Override - public boolean isReady() { - // Don't block playback whilst subtitles are loading. - // Note: To change this behavior, it will be necessary to consider [Internal: b/12949941]. - return true; - } - - private void releaseBuffers() { - nextInputBuffer = null; - nextSubtitleEventIndex = C.INDEX_UNSET; - if (subtitle != null) { - subtitle.release(); - subtitle = null; + @Override + protected void onDisabled() { + streamFormat = null; + finalStreamEndPositionUs = C.TIME_UNSET; + clearOutput(); + releaseDecoder(); } - if (nextSubtitle != null) { - nextSubtitle.release(); - nextSubtitle = null; + + @Override + public boolean isEnded() { + return outputStreamEnded; } - } - private void releaseDecoder() { - releaseBuffers(); - checkNotNull(decoder).release(); - decoder = null; - decoderReplacementState = REPLACEMENT_STATE_NONE; - } - - private void initDecoder() { - waitingForKeyFrame = true; - decoder = decoderFactory.createDecoder(checkNotNull(streamFormat)); - } - - private void replaceDecoder() { - releaseDecoder(); - initDecoder(); - } - - private long getNextEventTime() { - if (nextSubtitleEventIndex == C.INDEX_UNSET) { - return Long.MAX_VALUE; - } - checkNotNull(subtitle); - return nextSubtitleEventIndex >= subtitle.getEventTimeCount() - ? Long.MAX_VALUE - : subtitle.getEventTime(nextSubtitleEventIndex); - } - - private void updateOutput(List cues) { - if (outputHandler != null) { - outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cues).sendToTarget(); - } else { - invokeUpdateOutputInternal(cues); - } - } - - private void clearOutput() { - updateOutput(Collections.emptyList()); - } - - @SuppressWarnings("unchecked") - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_UPDATE_OUTPUT: - invokeUpdateOutputInternal((List) msg.obj); + @Override + public boolean isReady() { + // Don't block playback whilst subtitles are loading. + // Note: To change this behavior, it will be necessary to consider [Internal: b/12949941]. return true; - default: - throw new IllegalStateException(); } - } - private void invokeUpdateOutputInternal(List cues) { - output.onCues(cues); - output.onCues(new CueGroup(cues)); - } + private void releaseBuffers() { + nextInputBuffer = null; + nextSubtitleEventIndex = C.INDEX_UNSET; + if (subtitle != null) { + subtitle.release(); + subtitle = null; + } + if (nextSubtitle != null) { + nextSubtitle.release(); + nextSubtitle = null; + } + } - /** - * Called when {@link #decoder} throws an exception, so it can be logged and playback can - * continue. - * - *

Logs {@code e} and resets state to allow decoding the next sample. - */ - private void handleDecoderError(SubtitleDecoderException e) { - Log.e(TAG, "Subtitle decoding failed. streamFormat=" + streamFormat, e); - clearOutput(); - replaceDecoder(); - } + private void releaseDecoder() { + releaseBuffers(); + checkNotNull(decoder).release(); + decoder = null; + decoderReplacementState = REPLACEMENT_STATE_NONE; + } + + private void initDecoder() { + waitingForKeyFrame = true; + decoder = decoderFactory.createDecoder(checkNotNull(streamFormat)); + } + + private void replaceDecoder() { + releaseDecoder(); + initDecoder(); + } + + private long getNextEventTime() { + if (nextSubtitleEventIndex == C.INDEX_UNSET) { + return Long.MAX_VALUE; + } + checkNotNull(subtitle); + return nextSubtitleEventIndex >= subtitle.getEventTimeCount() + ? Long.MAX_VALUE + : subtitle.getEventTime(nextSubtitleEventIndex); + } + + private void updateOutput(List cues) { + if (outputHandler != null) { + outputHandler.obtainMessage(MSG_UPDATE_OUTPUT, cues).sendToTarget(); + } else { + invokeUpdateOutputInternal(cues); + } + } + + private void clearOutput() { + updateOutput(Collections.emptyList()); + } + + @SuppressWarnings("unchecked") + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_UPDATE_OUTPUT: + invokeUpdateOutputInternal((List) msg.obj); + return true; + default: + throw new IllegalStateException(); + } + } + + private void invokeUpdateOutputInternal(List cues) { + // See https://github.com/google/ExoPlayer/issues/7934 + // SubripDecoder texts tend to be DIMEN_UNSET which pushes up the + // subs unlike WEBVTT which creates an inconsistency + + List fixedCues = cues.stream().map( + cue -> { + Cue.Builder builder = cue.buildUpon(); + + if (cue.line == DIMEN_UNSET) + builder.setLine(-1f, LINE_TYPE_NUMBER); + + return builder.setSize(DIMEN_UNSET).build(); + } + ).collect(Collectors.toList()); + + output.onCues(fixedCues); + output.onCues(new CueGroup(fixedCues)); + } + + /** + * Called when {@link #decoder} throws an exception, so it can be logged and playback can + * continue. + * + *

g!fC#;0+fen{k3i*HT`@Uz5%PpHG z!Cm^L3(OC+m%K|#bEPy-lJhBM~Nhg5ex48|bhgu~Jm{m@;L+cIq0UAwAc;aHLIwomF`!k@2Q^ugeMy&f_ zSc31DsJVrLY4A)4TZaQ{h#6An-F;==kvWO^ZZmP+N=1%o*ha+CLfQ1=T^X2wx{H6w)U8^Z5N0pAp7j5l9wy3OOan2%J6d} zl&_->J=#&70snd4wW``5qWzLV#728($1=`Xv+UT{$+|DKhr?Bw^o ziyqac0<8MrlZ`W7sYB!4;4yUB12uQzun$b+$y99s7~VP+H&oro~fG@pt7`nHdZ7LZ*ie zJ$h!QC~YEqbX2!?ElvKqY7N2J3pevcj>0B_%{xQSr6HdCXfibHKaW94gS@PHm9>3h z!+mV{v)$Q3A8iM;1a!Va=1ZQu1Ma%Oc^hBxXDUja_cB~~JV?T{OEt-K&mM7Dx)Ua4 zlYeP$b=9XiKAkr8MR^8aQeS%ID^{i3p(nQ^_aoUIj}p^7mRa=~CkkN(5=j(%=!yyt zmAyR(oG0!+%TAG$MkdzM-X9MrZ@on@RbW#~obSkI-oBzL>xe5R35q7|4^d><3Mtx;=x0*<9KreEZ4I#e#E=E@tWQp| z!o-!AjKMrW@iLLlPuME-0$fqGRKI@vej@Jx*}(ovkhgKjzY_7@$*aNg;IeW1F~w9xi-ZXDsAa%y zusdGbGh&f#w(onos@&|`!at21S&oOgga`#djFxy-Cg|-d$>ZH`YF!EUCnT$TLod0$ zpT%8K9W12#^09$Cc`ifu-s7IoXga zUp`v%@Q#T}cG#J3rmZ`ZODfdT+Zba$ob#Bw%+m8&%Jdx@NTS0lN3^@m?(9#*7BKl@ zN>I&7o8g*~g>9!l3z1zKkX{<(FYM+cKfV_dzp!NL@TF56(c;ELiNILo4E16!&-fU=wyAYk^R}=pQoQXPN diff --git a/.github/player.jpg b/.github/player.jpg deleted file mode 100644 index f6959cf31d27e5debc98e7d69b54688c589803a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49418 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�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

6R?OyVHq`Nv6g|; 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 T_iJ8Mtj&4^JWxLkp~e3JSo6U! diff --git a/.github/search.jpg b/.github/search.jpg deleted file mode 100644 index 784bec892550387282d3551e7ed8d830aa0a1cd8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152135 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 !Xcj4QAvn?KT(Ckvd+4 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ξ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

Logs {@code e} and resets state to allow decoding the next sample. + */ + private void handleDecoderError(SubtitleDecoderException e) { + Log.e(TAG, "Subtitle decoding failed. streamFormat=" + streamFormat, e); + clearOutput(); + replaceDecoder(); + } } From eb90b79bf9abfceedc9b9967e5d5260f84344745 Mon Sep 17 00:00:00 2001 From: SANCTI-afk <63229113+SANCTI-afk@users.noreply.github.com> Date: Sat, 3 Dec 2022 15:17:33 +0200 Subject: [PATCH 090/273] Arabic language update for latest keys (#231) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * arabicLanguage100% * Update strings.xml * Arabic Full * translated(preffVplayerBtn) * renamed homeBtn for arabic layout * Arabic language update No more typical × Common language instead ✓ * Arabic translation minor update Last commit ready to be merged * arabic language update for latest keys * ready to merge * last minor edit and ready to merge --- app/src/main/res/values-ar/strings.xml | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index bda3cf2a..b3676fa6 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -6,7 +6,7 @@ الملصق الرئيسي التالي عشوائي - ارجع للخلف + رجوع تغيير المصدر معاينة الخلفية @@ -38,7 +38,7 @@ …تحميل أشاهده - في الانتظار + في الإنتظار مكتمل مهمل أخطط لمشاهدته @@ -100,7 +100,7 @@ لون الخلفية لون النافذة نوع الحافة - ارتفاع الترجمة + إرتفاع الترجمة الخط حجم الخط @@ -111,7 +111,7 @@ لم يتم إعطاء موز تحديد اللغة تلقائيًا - تحميل اللغات + لغات التحميل لغة الترجمة إضغط بإستمرار لإعادة التعيين للإعدادات الافتراضية إستيراد خطوط بوضعها هنا %s @@ -185,9 +185,10 @@ إخفاء جودة الفيديو المختارة من نتائج البحث تحديث الإضافات تلقائيًا + تنزيل الإضافات تلقائيًا التحديث التلقائي البحث تلقائيًا عن التحديثات الجديدة عند البداية - التحديث إلى الاصدارات التجريبيه (بيتا) + التحديث إلى الاصدارات التجريبية (بيتا) البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط Github تطبيق روايات خفيف من نفس المطورين @@ -255,7 +256,7 @@ فيلم مسلسل - كارتون + كرتون @string/anime @string/ova تورنت @@ -353,7 +354,7 @@ عام زر العشوائي إظهار زر العشوائي علي الشاشة الرئيسية - لغات الموفر + لغات المزود واجهة التطبيق المحتوي المفضل تفعيل محتوي البالغين داخل المزودين المدعومين @@ -378,13 +379,13 @@ mal_key opensubtitles_key nginx_key - password123 - MyCoolUsername - hello@world.com + كلمة المرور + إسم المستخدم + البريد الإلكتروني 127.0.0.1 - MyCoolSite - example.com - Language code (en) + إسم الموقع + رابط الموقع + اللغة (الإنجليزية) + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/background_card" + android:layout_width="114dp" + android:layout_height="180dp" + android:layout_margin="2dp" + android:layout_marginBottom="2dp" + android:elevation="10dp" + android:foreground="@drawable/outline_drawable" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius"> + android:id="@+id/imageView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> + android:id="@+id/title_shadow" + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_gravity="bottom" + android:clickable="false" + android:focusable="false" + android:src="@drawable/title_shadow" + tools:ignore="ContentDescription" /> + android:id="@+id/imageText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="@color/textColor" + android:textStyle="bold" + tools:text="The Perfect Run" /> + android:id="@+id/search_item_download_play" + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_gravity="center" + android:src="@drawable/play_button" /> + android:id="@+id/watchProgress" + style="@android:style/Widget.Material.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="5dp" + android:layout_gravity="bottom" + android:layout_marginBottom="-1.5dp" + android:progressBackgroundTint="?attr/colorPrimary" + android:progressTint="?attr/colorPrimary" + tools:progress="50" /> - + + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="end" + android:orientation="vertical"> + android:id="@+id/text_is_dub" + style="@style/DubButton" + android:layout_gravity="end" /> + android:id="@+id/text_is_sub" + style="@style/SubButton" + android:layout_gravity="end" /> + android:id="@+id/text_flag" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> diff --git a/app/src/main/res/layout/home_result_grid_expanded.xml b/app/src/main/res/layout/home_result_grid_expanded.xml index 3cf4e7f9..b697c1de 100644 --- a/app/src/main/res/layout/home_result_grid_expanded.xml +++ b/app/src/main/res/layout/home_result_grid_expanded.xml @@ -5,58 +5,58 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/background_card" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="2dp" + android:backgroundTint="@color/transparent" + android:foreground="@drawable/outline_drawable" + app:cardCornerRadius="@dimen/rounded_image_radius" + app:cardElevation="0dp"> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:id="@+id/image_holder" + android:layout_width="114dp" + android:layout_height="180dp" + android:elevation="10dp" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius"> + android:id="@+id/imageView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> + android:id="@+id/search_item_download_play" + android:layout_width="60dp" + android:layout_height="60dp" + android:layout_gravity="center" + android:src="@drawable/play_button" /> + android:id="@+id/watchProgress" + style="@android:style/Widget.Material.ProgressBar.Horizontal" + android:layout_width="match_parent" + android:layout_height="5dp" + android:layout_gravity="bottom" + android:layout_marginBottom="-1.5dp" + android:progressBackgroundTint="?attr/colorPrimary" + android:progressTint="?attr/colorPrimary" + tools:progress="50" /> - + + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="end" + android:orientation="vertical"> + android:id="@+id/text_is_dub" + style="@style/DubButton" + android:layout_gravity="end" /> + android:id="@+id/text_is_sub" + style="@style/SubButton" + android:layout_gravity="end" /> + android:id="@+id/text_flag" + style="@style/SearchBox" + android:layout_gravity="end" + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> + android:id="@+id/imageText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:minLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="?attr/textColor" + android:textSize="13sp" + tools:text="The Perfect Run\nThe Perfect Run\nhello" /> diff --git a/app/src/main/res/layout/search_result_grid.xml b/app/src/main/res/layout/search_result_grid.xml index 98fe5812..f3c35ca4 100644 --- a/app/src/main/res/layout/search_result_grid.xml +++ b/app/src/main/res/layout/search_result_grid.xml @@ -1,98 +1,89 @@ + android:clickable="true" + android:focusable="true" + android:foreground="@drawable/outline_drawable" + android:orientation="vertical"> + + + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" /> - - + android:id="@+id/title_shadow" + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_gravity="bottom" + android:clickable="false" + android:focusable="false" + android:src="@drawable/title_shadow" + tools:ignore="ContentDescription" /> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="@color/textColor" + android:textStyle="bold" /> - + + + + + + + - - - - - - + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 710c6cf8..eb859af5 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -1,90 +1,81 @@ + android:clickable="true" + android:focusable="true" + android:foreground="@drawable/outline_drawable" + android:orientation="vertical"> + android:id="@+id/background_card" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_margin="2dp" + android:layout_marginBottom="2dp" + android:elevation="10dp" + app:cardBackgroundColor="?attr/primaryGrayBackground" + app:cardCornerRadius="@dimen/rounded_image_radius"> + android:layout_height="match_parent" + android:contentDescription="@string/search_poster_img_des" + android:duplicateParentState="true" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:scaleType="centerCrop" + tools:src="@drawable/example_poster" /> - + + + + + + + - - - - - - + android:background="@color/transparent" + android:textSize="20sp" + android:visibility="gone" + tools:text="🇸🇪" + tools:visibility="visible" /> + android:id="@+id/imageText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:ellipsize="end" + android:gravity="center" + android:maxLines="2" + android:minLines="2" + android:paddingStart="5dp" + android:paddingTop="5dp" + android:paddingEnd="5dp" + android:paddingBottom="5dp" + android:textColor="?attr/textColor" + android:textSize="13sp" + tools:text="The Perfect Run\nThe Perfect Run" /> \ 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 c3e51ab5..7c2e798b 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -25,13 +25,13 @@ #FFF #000 - #3d50fa + #121950 #121213 #3B65F5 - #F54A3B + #571711 #F53B66 - #3BF585 + #BEC8FF ?attr/colorPrimaryDark #FF6F63 diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index e748868b..dac8b9cd 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -16,5 +16,4 @@ 2000 3dp - \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 9c2f62fc..4fde7ae8 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -97,6 +97,24 @@ @color/white @color/whiteText + + + + + + - + + + + + + + + + From 2771dcb6124726ade7aec8a9e46655f5ff6987f9 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:38:55 +0000 Subject: [PATCH 200/273] update version code --- app/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 808c0cc3..93e386b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 33 - versionCode = 56 - versionName = "3.5.0" + versionCode = 57 + versionName = "4.0.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") @@ -253,4 +253,4 @@ tasks.withType().configureEach { } } } -} \ No newline at end of file +} From 9905618a47d9f3d47abe5a4f627fce0733e1f072 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:15:28 +0100 Subject: [PATCH 201/273] Added safe mode file as a last resort --- .../lagradost/cloudstream3/MainActivity.kt | 9 +++++++-- .../cloudstream3/plugins/PluginManager.kt | 19 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 5720b7a7..eddec15e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -347,7 +347,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } - var lastPopup : SearchResponse? = null + var lastPopup: SearchResponse? = null fun loadPopup(result: SearchResponse) { lastPopup = result viewModel.load( @@ -716,7 +716,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) - if (lastError == null) { + + if (PluginManager.checkSafeModeFile()) { + normalSafeApiCall { + showToast(this, R.string.safe_mode_file, Toast.LENGTH_LONG) + } + } else if (lastError == null) { ioSafe { getKey(USER_SELECTED_HOMEPAGE_API)?.let { homeApi -> mainPluginsLoadedEvent.invoke(loadSinglePlugin(this@MainActivity, homeApi)) 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 54fe5d75..28dfc092 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -144,8 +144,10 @@ object PluginManager { return getKey(PLUGINS_KEY_LOCAL) ?: emptyArray() } - private val LOCAL_PLUGINS_PATH = - Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/plugins" + private val CLOUD_STREAM_FOLDER = + Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/" + + private val LOCAL_PLUGINS_PATH = CLOUD_STREAM_FOLDER + "plugins" public var currentlyLoading: String? = null @@ -421,6 +423,19 @@ object PluginManager { afterPluginsLoadedEvent.invoke(forceReload) } + /** + * This can be used to override any extension loading to fix crashes! + * @return true if safe mode file is present + **/ + fun checkSafeModeFile(): Boolean { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + return files?.any() ?: false + } + /** * @return True if successful, false if not * */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cee6bccc..a4375ce3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -630,4 +630,5 @@ Open with Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one + Safe mode file found!\nNot loading any extensions on startup until file is removed. From fd2648df459e4cdc0089ea5119de6da349d211c5 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:31:16 +0100 Subject: [PATCH 202/273] made the checkSafeModeFile() crash-proof --- .../cloudstream3/plugins/PluginManager.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) 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 28dfc092..3533d6a8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -428,12 +428,14 @@ object PluginManager { * @return true if safe mode file is present **/ fun checkSafeModeFile(): Boolean { - val folder = File(CLOUD_STREAM_FOLDER) - if (!folder.exists()) return false - val files = folder.listFiles { _, name -> - name.equals("safe", ignoreCase = true) - } - return files?.any() ?: false + return normalSafeApiCall { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return@normalSafeApiCall false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + files?.any() + } ?: false } /** From 6e9b1cb855fa298f0df3c8b015a4cd8583821ddc Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:51:25 +0100 Subject: [PATCH 203/273] Made source dialog fullscreen and added some Extractors --- .../cloudstream3/extractors/Filesim.kt | 41 ++++++++++++++----- .../cloudstream3/extractors/GuardareStream.kt | 5 +++ .../cloudstream3/ui/player/GeneratorPlayer.kt | 10 ++--- .../cloudstream3/utils/ExtractorApi.kt | 2 + .../layout/player_select_source_and_subs.xml | 2 + 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 8e3dc730..bc910a7e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -1,32 +1,51 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import java.net.URI + +class FileMoon : Filesim() { + override val mainUrl = "https://filemoon.to" + override val name = "FileMoon" +} open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" override val requiresReferer = false - override suspend fun getUrl(url: String, referer: String?): List { - val sources = mutableListOf() + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { with(app.get(url).document) { - this.select("script").map { script -> + this.select("script").forEach { script -> if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]") - tryParseJson>("[$data]")?.map { - M3u8Helper.generateM3u8( - name, - it.file, - "$mainUrl/", - ).forEach { m3uData -> sources.add(m3uData) } + val data = getAndUnpack(script.data()) + val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach + val fixedData = foundData.replace("file:", """"file":""") + + parseJson>("[$fixedData]").forEach { + callback.invoke( + ExtractorLink( + name, + name, + it.file, + "$mainUrl/", + Qualities.Unknown.value, + URI(it.file).path.endsWith(".m3u8") + ) + ) } } } } - return sources } private data class ResponseSource( diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt index f25cb5ba..2adc00d5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt @@ -6,6 +6,11 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +class Vanfem : GuardareStream() { + override var name = "Vanfem" + override var mainUrl = "https://vanfem.com/" +} + class CineGrabber : GuardareStream() { override var name = "CineGrabber" override var mainUrl = "https://cinegrabber.com" 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 e15dcee6..67f58195 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 @@ -11,9 +11,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.EditorInfo import android.widget.* -import android.widget.TextView.OnEditorActionListener import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.core.animation.addListener @@ -528,7 +526,7 @@ class GeneratorPlayer : FullScreenPlayer() { } } - var selectSourceDialog: AlertDialog? = null + var selectSourceDialog: Dialog? = null // var selectTracksDialog: AlertDialog? = null override fun showMirrorsDialogue() { @@ -540,10 +538,8 @@ class GeneratorPlayer : FullScreenPlayer() { player.handleEvent(CSPlayerEvent.Pause) val currentSubtitles = sortSubs(currentSubs) - val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) - .setView(R.layout.player_select_source_and_subs) - - val sourceDialog = sourceBuilder.create() + val sourceDialog = Dialog(ctx, R.style.AlertDialogCustomBlack) + sourceDialog.setContentView(R.layout.player_select_source_and_subs) selectSourceDialog = sourceDialog diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index bd4f8705..1ad3639b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -291,6 +291,7 @@ val extractorApis: MutableList = arrayListOf( Supervideo(), GuardareStream(), CineGrabber(), + Vanfem(), // StreamSB.kt works // SBPlay(), @@ -321,6 +322,7 @@ val extractorApis: MutableList = arrayListOf( DesuDrive(), Filesim(), + FileMoon(), Linkbox(), Acefile(), SpeedoStream(), diff --git a/app/src/main/res/layout/player_select_source_and_subs.xml b/app/src/main/res/layout/player_select_source_and_subs.xml index 369f6776..067e4ad5 100644 --- a/app/src/main/res/layout/player_select_source_and_subs.xml +++ b/app/src/main/res/layout/player_select_source_and_subs.xml @@ -44,6 +44,7 @@ android:nextFocusLeft="@id/sort_subtitles" android:nextFocusRight="@id/apply_btt" android:requiresFadingEdge="vertical" + tools:layout_height="100dp" tools:listitem="@layout/sort_bottom_single_choice" /> @@ -117,6 +118,7 @@ android:nextFocusLeft="@id/sort_providers" android:nextFocusRight="@id/cancel_btt" android:requiresFadingEdge="vertical" + tools:layout_height="200dp" tools:listfooter="@layout/sort_bottom_footer_add_choice" tools:listitem="@layout/sort_bottom_single_choice" /> From c7c5fa250e177dbf22a9ea463c721c63f3feb79a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 31 Jan 2023 09:12:25 +0100 Subject: [PATCH 204/273] Translated using Weblate (Polish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 96.5% (562 of 582 strings) Translated using Weblate (Russian) Currently translated at 91.4% (532 of 582 strings) Translated using Weblate (Greek) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 88.3% (514 of 582 strings) Translated using Weblate (Russian) Currently translated at 87.9% (512 of 582 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (German) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Italian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Polish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Italian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Co-authored-by: Aitor Salaberria Co-authored-by: Alex Georgiou Co-authored-by: Cliff Heraldo Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Hosted Weblate Co-authored-by: JL Pilgram Co-authored-by: Julian Co-authored-by: NickSkier Co-authored-by: Rex_sa Co-authored-by: Sdarfeesh Co-authored-by: Skrripy Co-authored-by: Translator-3000 Co-authored-by: eightyy8 Co-authored-by: gallegonovato Co-authored-by: gnu-ewm Co-authored-by: kaajjo Co-authored-by: tuan041 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 17 ++++ app/src/main/res/values-de/strings.xml | 17 ++++ app/src/main/res/values-el/strings.xml | 20 ++++- app/src/main/res/values-es/strings.xml | 17 ++++ app/src/main/res/values-in/strings.xml | 21 ++++- app/src/main/res/values-it/strings.xml | 17 ++++ app/src/main/res/values-pl/strings.xml | 17 ++++ app/src/main/res/values-ru/strings.xml | 107 ++++++++++++++++++++----- app/src/main/res/values-uk/strings.xml | 15 ++++ app/src/main/res/values-vi/strings.xml | 53 ++++++++---- app/src/main/res/values-zh/strings.xml | 17 ++++ 11 files changed, 278 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3a0c97e7..f318478e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -521,4 +521,21 @@ بدأ التحديث تم تنزيل الإضافة إزالة من المشاهدة + الترتيب الأبجدي (من الألف إلى الياء) + اختر المكتبة + المتصفح + محدث (من الأحدث إلى الأقدم) + يبدو أن هذه القائمة فارغة ، حاول التبديل إلى قائمة أخرى + التقييم (من الأعلى إلى الأدنى) + التقييم (من الأدنى إلى الأعلى) + الترتيب الأبجدي (من ي إلى أ) + يبدو أن مكتبتك فارغة :( +\nتسجيل الدخول إلى حساب مكتبة أو إضافة عروض إلى مكتبتك المحلية + محدث (من القديم إلى الجديد) + فرز حسب + افرز + فتح بواسطة + المكتبة + تم العثور على ملف الوضع الآمن! +\nلا يتم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف. \ 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 00aa5b97..63ed5444 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -489,4 +489,21 @@ Die Anwendung wird beim Beenden aktualisiert Das Plugin wurde heruntergeladen Von geschaut entfernen + Bibliothek + Browser + Sortieren nach + Sortieren + Bewertung (gut bis schlecht) + Bewertung (schlecht bis gut) + Aktualisiert (neu bis alt) + Aktualisiert (alt bis neu) + Alphabetisch (A bis Z) + Alphabetisch (Z bis A) + Bibliothek auswählen + Öffnen mit + Sieht aus, als wäre deine Bibliothek leer :( +\nMelde dich mit einem Bibliothekskonto an oder füge Titel zu deiner lokalen Bibliothek hinzu + Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln. + Datei für abgesicherten Modus gefunden! +\nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird. \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index dc7088cc..0d0b7fb2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -314,7 +314,7 @@ Αναφορά κατάρρευσης Τι θα θέλατε να δείτε Έγινε - Πρόσθετα + Extensions Προσθήκη αποθετηρίου Όνομα αποθετηρίου Σύνδεσμος αποθετηρίου @@ -490,4 +490,22 @@ Το πρόσθετο κατέβει Ενημέρωση ξεκίνησε Η εφαρμογή θα ενημερωθεί κατά την έξοδο + Αλφαβητικά (Ω προς Α) + Ταξινόμηση + Κριτική (Χαμηλή προς Υψηλή) + Ενημερωμένο (Καινούριο προς παλιό) + Ενημερωμένο (Παλιό προς Καινούργιο) + Βιβλιοθήκη + Κριτική (Υψηλή προς χαμηλή) + Ταξινόμηση με βάση + Αλφαβητικά (Α προς Ω) + Διάλεξε βιβλιοθήκη + Φαίνεται πως η λίστα είναι άδεια, δοκίμασε να μεταβείς σε μία άλλη + Αφαίρεση από παρακολουθημένα + Περιηγητής + Άνοιγμα με + Φαίνεται πως η βιβλιοθήκη σου είναι άδεια :( +\nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου + Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! +\nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο. \ 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 08ae5bf1..37e5c431 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -489,4 +489,21 @@ Actualización iniciada Complemento descargado Quitar de visto + Ordenar por + Ordenar + Valoración (más a menos) + Valoración (menos a más) + Actualizado (nuevo a viejo) + Actualizado (viejo a nuevo) + Alfabéticamente (A a Z) + Navegador + Biblioteca + Parece que esta lista está vacía, intenta cambiar a otra + Alfabéticamente (Z a A) + Seleccionar biblioteca + Abrir con + Parece que tu biblioteca está vacía :( +\nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local + ¡Se encontró un archivo en modo seguro! +\nNo cargar ninguna extensión al inicio hasta que se elimine el archivo. \ 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 83dc6ee9..96c5950b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -453,7 +453,7 @@ Semua fitur tambahkan dimatikan karena crash, untuk memudahkanmu mencari penyebab crash. Kode bahasa (en) Ambil dari internet - Putar vidio di bahasa ini + Putar video di bahasa ini Tambah Repositori Pilih ini untuk menghapus semua repositori plugin Lewati pengaturan @@ -483,7 +483,7 @@ Gerakan Beberapa perangkat tidak mendukung penginstal paket mode baru. Coba mode lama jika pembaruan tidak dapat diinstal. Aksi - Referensi + Referer Ya Pasang dulu fitur tambahan Semua Bahasa @@ -512,4 +512,21 @@ Aplikasi akan diperbaharui pada saat keluar Pembaharuan Dimulai Hapus dari tontonan + Browser + Pilih pustaka + Yahh daftar pustaka kamu kosong :( +\nMasuk ke akun pustaka atau tambah perlihatkan ke lokal pustaka kamu + Pustaka + Urutkan berdasar + Urutkan + Peringkat (Rendah ke Tinggi) + Update (Lama ke Terbaru) + Peringkat (Tinggi ke Rendah) + Update (Terbaru ke Lama) + Abjad (A ke Z) + Abjad (Z ke A) + Buka dengan + Yahh daftar ini kosong, coba ganti ke yang lain + Mode aman file ditemukan! +\nTidak memuat ekstensi pada startup sampai berkas dihapus. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b4ba292e..419818a2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -511,4 +511,21 @@ Aggiornamento avviato Plugin scaricato Rimuovi dai già visti + Browser + Ordina per + Punteggio (Decrescente) + Punteggio (Crescente) + Aggiornato (Da nuovo a vecchio) + Aggiornato (Da vecchio a nuovo) + Alfabetico (A - Z) + Alfabetico (Z - A) + Sembra che la tua libreria sia vuota :( +\nAccedi a un account di libreria o aggiungi degli show alla tua libreria locale + Seleziona libreria + Apri con + Libreria + Ordina + Sembra che questa lista sia vuota, prova a passare a un\'altra + File \"safe mode\" trovato! +\nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso. \ 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 244ae2e1..e4b74300 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -492,4 +492,21 @@ Rozpoczęto aktualizację Pobrano rozszerzenie Usuń z obejrzanych + Przeglądarka + Data aktualizacji (od nowego do starego) + Sortuj według + Sortuj + Otwórz za pomocą + Ocena (od najwyższej do najniższej) + Ocena (od najniższej do najwyższej) + Data aktualizacji (od starego do nowego) + Alfabetycznie (od A do Z) + Alfabetycznie (od Z do A) + Wybierz bibliotekę + Biblioteka + Wygląda na to, że twoja biblioteka jest pusta :( +\nZaloguj się na swoje konto lub dodaj programy do swojej lokalnej biblioteki + Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną + Znaleziono plik trybu bezpiecznego. +\nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty. \ 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 39e74794..537bdb7d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -25,12 +25,12 @@ Серия %d будет выпущен в Плакат \@нить/результат_плокат_картинка_ - Серия плакат - Главный плакат + Постер Эпизода + Главный постер Следующий случайный Вернуться Изменить поставщика - Фон предпросмотр + Предпросмотр фона Скорость (%.2fx) Оценили: %.1f Новое обновление найдено! @@ -48,31 +48,31 @@ Поиск %s… Нет данных Дополнительные опции - Следующий серия + Следующий эпизод Жанры Поделиться - Открыть в браузер + Открыть в браузере Пропустить загрузку Просмотр Приостановленно Завершено Брошенный - План по смотреть + План посмотреть Никто Пересмотрю Смотреть фильм - Проиграть трейлер + Воспроизвести трейлер Воспроизвести Livestream Источники Субтитры - Проиграть серия + Воспроизвести эпизод Повторная попытка подключение… Вернуться - Скачали + Скачано Скачивание Скачать приостановленный Скачать начатый - Скачать отменено + Скачать отменённый Скачать выполнено Инфо Обновление началось @@ -116,8 +116,8 @@ Удалить файл Проиграть файл Внутренняя память - Скачать резюме - Приостановить скачать + Продолжить Скачать + Приостановить скачивание Отключить автоматическое информирование об ошибках Импортируйте шрифты поместив их в %s Продолжить смотреть @@ -174,7 +174,7 @@ Э Эпизоды не найдены Удалить файл - Возобновить + Продолжить -30 +30 Это будет удалено безвозвратно%s @@ -193,7 +193,7 @@ Другое Ошибка загрузки, проверьте разрешения хранилища Копировать ссылку - Автоматическая загрузка + Автоскачивание Загрузка. Зеркало Сезон Аниме приложение от тех же разработчиков @@ -217,7 +217,7 @@ Торрент Документальный Азиатская драма - Общий + Основные Провайдеры Макет Расширения @@ -240,7 +240,7 @@ Обновление не найдено Изменить размер Источник - Проверьте наличие обновления + Проверить обновления Клон сайта DNS через HTTPS Удалить сайт @@ -248,7 +248,7 @@ Синхронизация субтитров Добавить клон существующего сайта с другим URL-адресом Используется для обхода блокировок интернет провайдера - Путь загрузки + Путь скачивания учитывая бенен Обновить Основной цвет @@ -304,7 +304,7 @@ Пропустить это обновление URL-адрес NGINX-сервера Создать учётную запись - Добавить трекинг + Добавить слежение Добавлено %s Синхронизировать Оценено @@ -393,7 +393,7 @@ Отключено: %d %s %s %s аутентифицировано - Не удалось перейти к %s + Не удается логин на %s Макс Минимум Очертание @@ -416,20 +416,83 @@ Главное Источник Случайный - Скоро будет… + Скоро… Этикетка Sub Фон - Вид + Oтoбpaжeниe Трейлер %s (отключено) Следующий В CloudStream по умолчанию не установлены сайты. Вам необходимо установить сайты из репозиториев. \n -\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем связать сайт репозитория в приложении. +\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем привязать сайт репозитория в приложении. \n \nПрисоединяйтесь к нашему Discord или ищите в интернете. Недопустимые данные Разрешение и название Предыдущий Разрешение + Браузер + Библиотека + Обновленный (старый - новый) + Алфавитный (А - Я) + Алфавитный (Я - А) + Выбрать библиотеку + Открыть с + Похоже, ваша библиотека пуста :( +\nВойдите в аккаунт с библиотекой или добавьте сериалы в локальную библиотеку + Сортировка + Открытый список + Рейтинг (высокий - низкий) + Рейтинг (низкий - высокий) + Обновленный (новый - старый) + Сортировать по + PackageInstaller + Кодировка субтитров + Загрузить из файла + Рейтинг: %s + Скачано %d %s + Все %s уже скачаны + Начата загрузка %d %s… + Не скачано: %d + Скачать все плагины из этого репозитория\? + Включен безопасный режим + Скачано: %d + Обновлено %d плагинов + Загрузить из интернета + Загрузка обновления приложения… + Недопустимый URL + Применить при перезапуске + Отчеты ошибках + Что вы хотите увидеть + Смотрите видео на этих языках + Скачано файл + Изображение постера + Пакетная загрузка + Скачайте список сайтов, который вы хотите использовать + Отображать Аниме с Дубляжом/Субтитрами + Включить NSFW на поддерживаемых провайдерах + Убрать скрытые субтитры из субтитров + Дополнительно + Изменить вид интерфейса, чтобы соответствовать устройству + Аудио дорожки + Это также удалит все плагины репозитория + Просмотреть репозитории сообщества + Видео дорожки + Все расширения были отключены из-за сбоя, чтобы помочь вам найти то, которое вызывает проблемы. + Повтор + Слишком много текста. Не удалось сохранить в буфер обмена. + Установка обновления приложения… + Не удалось установить новую версию приложения + Файл безопасного режима найден! +\nНе загружаются никакие расширения при запуске, пока файл не будет удален. + Приложение будет обновлено после выхода + Похоже, этот список пуст, попробуйте переключиться на другой + Все субтитры заглавными + Показывать всплывающие окна для пропуска вступления/заключения + Фильтровать по предпочитаемому языку медиа + Неверный ID + Ссылка на стрим + Отображать рандомную кнопку на Главной странице + Рандомная кнопка \ 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 93e51c84..821d062a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -489,4 +489,19 @@ Програму не знайдено Змішаний опенінг Видалити з переглянутого + За оновленням (від старого до нового) + За оновленням (від нового до старого) + Бібліотека + Сортувати + За рейтингом (від високого до низького) + Сортувати за + За алфавітом (від А до Я) + За рейтингом (від низького до високого) + Схоже, ваша бібліотека порожня :( +\nУвійдіть в обліковий запис бібліотеки або додайте серіали до вашої локальної бібліотеки + За алфавітом (від Я до А) + Виберіть бібліотеку + Відкрити з + Браузер + Схоже, цей список порожній, спробуйте перейти до іншого \ 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 3aa5cf69..db647b5d 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -66,7 +66,7 @@ Tải thành công Trực tiếp Đã có lỗi xảy ra - Bộ nhớ máy + Bộ nhớ trong Lồng Tiếng Phụ Đề Xóa Tệp @@ -118,7 +118,7 @@ Không tìm thấy thông tin Hiển thị Logcat 🐈 Chế độ cửa sổ nhỏ - Tiếp tục xem phim khi thoát app hoặc đang tìm kiếm + Tiếp tục xem phim khi thoát ứng dụng hoặc khi tìm kiếm Bật nút thu phóng khi xem Xóa khoảng đen của phim Phụ đề @@ -159,7 +159,7 @@ Không gửi dữ liệu Hiển thị tập phụ cho anime Hiển thị trailer - Hiển thị poster từ kitsu + Hiển thị poster từ Kitsu Ẩn chất lượng video khi tìm kiếm Tự động cập nhật plugin Hiển thị thông báo cập nhật App @@ -210,7 +210,7 @@ Không có phụ đề Mặc Định Còn trống - Đã dùng + Đã sử dụng App Phim Lẻ @@ -228,7 +228,7 @@ Phim Lẻ Phim Bộ Hoạt Hình - \@string/anime + Anime \@string/ova Torrent Phim Tài Liệu @@ -261,7 +261,7 @@ Khóa Thu Phóng Tuỳ chọn - Tập tiếp + Tua nhanh Không hiện lại Bỏ qua Cập nhật @@ -272,8 +272,8 @@ Thời lượng bộ nhớ đệm Dung lượng video cache Xoá hình ảnh và video - Sẽ gây lỗi nếu đặt quá cao. Không thay đổi nếu máy có dung lượng ram thấp, chẳng hạn như Android TV hoặc điện thoại cũ - Sẽ thể gây lỗi trên các máy có dung lượng lưu trữ thấp, chẳng hạn như thiết bị Android TV nếu bạn đặt nó quá cao + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng ram thấp như Android TV. + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. DNS over HTTPS Rất hữu ích để bỏ chặn ISP Sao chép trang web @@ -410,7 +410,7 @@ Đã xoá plugin Không tải được %s 18+ - Bắt đầu tải %d %s + Bắt đầu tải %d %s… Tải xuống %d %s thành công Toàn bộ %s đã được tải xuống Tải hàng loạt @@ -422,7 +422,11 @@ Đã tải: %d Đã vô hiệu: %d Không tải: %d - Thêm kho lưu trữ để cài tiện ích + CloudStream không có sẵn trang web nào. Bạn cần cài đặt các trang web từ kho lưu trữ. +\n +\nDo Sky UK Limited đã gỡ xuống theo DMCA một cách thiếu suy nghĩ 🤮 chúng tôi không thể cài sẵn trang web. +\n +\nHãy tham gia Discord của chúng tôi hoặc tìm kiếm trực tuyến. Xem kho lưu trữ của cộng đồng Danh sách công khai In hoa toàn bộ phụ đề @@ -437,18 +441,18 @@ Xem thông tin sự cố Lịch sử Đánh dấu là đã xem - Tự động tải plugin + Tự động tải xuống plugin Thiết lập lại Bộ cài APK Một số máy không hỗ trợ trình cài đặt gói mới. Hãy thử tùy chọn cũ nếu các bản cập nhật không cài đặt. %s %d%s - Xem Trailer - Tự động tải plugins còn thiếu. + Xem giới thiệu + Tự động tải plugin còn thiếu. Bắt đầu cập nhật Liên kết Danh sách HLS Trình phát ưu tiên - Trình phát mặc địng + Trình phát mặc định Đánh giá: %s Không Phiên bản @@ -487,7 +491,7 @@ Danh đề Giới thiệu Xoá lịch sử - Hiển thị cửa sổ tua cho mở đầu/kết thúc + Hiển thị nút tua nhanh cho mở đầu/kết thúc Văn bản quá dài. Không thể lưu vào bộ nhớ tạm. Xoá khỏi đã xem Bạn có chắc muốn thoát\? @@ -496,4 +500,23 @@ Đang cài bản cập nhật… Không thể cài đặt phiên bản mới Ứng dụng sẽ được cập nhật khi thoát + Thư viện + Trình duyệt + Plugin đã tải + Mặc định + Tải lên (Mới đến Cũ) + Tải lên (Cũ đến Mới) + Thư viện của bạn đang trống :( +\nHãy đăng nhập vào thư viện hoặc thêm phim vào thư viện cục bộ + Mở với + Siêu dữ liệu không có sẵn, video sẽ không được tải nếu nó không tồn tại trên trang web. + PackageInstaller + Sắp xếp + Xếp hạng (Cao đến Thấp) + Xếp hạng (Thấp đến Cao) + Chữ cái (Z đến A) + Sắp xếp + Có vẻ như danh sách này trống, hãy thử chuyển sang danh sách khác + Chữ cái (A đến Z) + Chọn Thư viện \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 97a48597..ece917d9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -537,4 +537,21 @@ 应用退出后将会更新 插件已下载 从已观看中移除 + 发现安全模式文件! +\n启动时不加载任何扩展,直到文件被删除。 + 浏览器 + + 排序方式 + 排序 + 评分(从高到低) + 评分(从低到高) + 更新(从新到旧) + 更新(从旧到新) + 字母排序(从 A 到 Z) + 字母排序(从 Z 到 A) + 选择库 + 打开方式 + 看来您的库是空的 :( +\n登录库账户或添加节目到您的本地库 + 看来此列表是空的,请尝试切换到另一个 \ No newline at end of file From b26a41bdaf8aeab10a7038c991327747bdd74cf5 Mon Sep 17 00:00:00 2001 From: hexated Date: Tue, 31 Jan 2023 14:26:34 +0700 Subject: [PATCH 205/273] fixed VidSrcExtractor --- .../lagradost/cloudstream3/extractors/VidSrcExtractor.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index b910f9dd..a27bf188 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -59,8 +59,8 @@ open class VidSrcExtractor : ExtractorApi() { if (datahash.isNotBlank()) { val links = try { app.get( - "$absoluteUrl/src/$datahash", - referer = "https://source.vidsrc.me/" + "$absoluteUrl/srcrcp/$datahash", + referer = "https://rcp.vidsrc.me/" ).url } catch (e: Exception) { "" @@ -71,7 +71,7 @@ open class VidSrcExtractor : ExtractorApi() { serverslist.amap { server -> val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/") - if (linkfixed.contains("/pro")) { + if (linkfixed.contains("/prorcp")) { val srcresponse = app.get(server, referer = absoluteUrl).text val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)") val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@amap From 490381451b28473fcfc41002856e5daa4c1ec131 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:57:11 +0100 Subject: [PATCH 206/273] [skip ci] label issues if provider mentioned --- .github/workflows/issue_action.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml index 28b737b3..2a54857c 100644 --- a/.github/workflows/issue_action.yml +++ b/.github/workflows/issue_action.yml @@ -53,6 +53,18 @@ jobs: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM). Found provider name: `${{ steps.provider_check.outputs.name }}` + - name: Label if mentions provider + if: steps.provider_check.outputs.name != 'none' + uses: actions/github-script@v6 + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["possible provider issue"] + }) - name: Add eyes reaction to all issues uses: actions-cool/emoji-helper@v1.0.0 with: From b0921161a3c31c46079c4791f575e538c0b1e153 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Tue, 31 Jan 2023 23:43:29 +0100 Subject: [PATCH 207/273] Nicehttp version bump --- 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 93e386b6..3c855d28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -190,7 +190,7 @@ dependencies { // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") - implementation("com.github.Blatzar:NiceHttp:0.4.1") + implementation("com.github.Blatzar:NiceHttp:0.4.2") // To fix SSL fuckery on android 9 implementation("org.conscrypt:conscrypt-android:2.2.1") // Util to skip the URI file fuckery 🙏 From 99887534327c86bcc653dfbc84a24917d34b0fe5 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Thu, 2 Feb 2023 01:15:24 +0100 Subject: [PATCH 208/273] Library and Light mode improvements. --- .../lagradost/cloudstream3/ui/library/LibraryFragment.kt | 4 +++- .../com/lagradost/cloudstream3/ui/result/ResultFragment.kt | 1 + app/src/main/res/layout/fragment_library.xml | 6 +++--- app/src/main/res/layout/search_result_grid_expanded.xml | 2 +- app/src/main/res/values/styles.xml | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) 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 1c6af447..d7c06c4e 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 @@ -333,8 +333,10 @@ class LibraryFragment : Fragment() { handler.postDelayed(stopLoading, 300) savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> + if (currentPos < 0) return@let viewpager?.setCurrentItem(currentPos, false) - savedInstanceState.remove(VIEWPAGER_ITEM_KEY) + // Using remove() sets the key to 0 instead of removing it + savedInstanceState.putInt(VIEWPAGER_ITEM_KEY, -1) } // Since the animation to scroll multiple items is so much its better to just hide diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 2e2e46b7..68dd1c0e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -974,6 +974,7 @@ open class ResultFragment : ResultTrailerPlayer() { chip.isCheckable = false chip.isFocusable = false chip.isClickable = false + chip.setTextColor(context.colorFromAttribute(R.attr.textColor)) addView(chip) } } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index f9012148..985d055d 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -166,12 +166,12 @@ app:layout_scrollFlags="noScroll" app:tabGravity="center" app:tabIndicator="@drawable/indicator_background" - app:tabIndicatorColor="@color/textColor" + app:tabIndicatorColor="?attr/white" app:tabIndicatorGravity="center" app:tabIndicatorHeight="30dp" app:tabMode="scrollable" - app:tabSelectedTextColor="@color/lightTextColor" + app:tabSelectedTextColor="?attr/primaryBlackBackground" app:tabTextAppearance="@style/TabNoCaps" - app:tabTextColor="@color/textColor" /> + app:tabTextColor="?attr/textColor" /> diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 47fd7cd3..cf6ab3b2 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -23,7 +23,7 @@ android:elevation="10dp" app:cardBackgroundColor="?attr/primaryGrayBackground" app:cardCornerRadius="@dimen/rounded_image_radius" - app:layout_constraintDimensionRatio="1:1.414" + app:layout_constraintDimensionRatio="1:1.5" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2540bf34..78c62c69 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -100,6 +100,7 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 61ff0c2b..7dd4c989 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -82,4 +82,7 @@ #515151 #FFFFFF #622C00 + + #48E484 + #ea596e \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 778f34c9..cb9d5508 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,6 +13,7 @@ fast_forward_button_time benene_count subtitle_settings_key + test_providers_key subtitle_settings_chromecast_key quality_pref_key player_pref_key @@ -195,6 +196,7 @@ No Plot Found No Description Found Show Logcat 🐈 + Log Picture-in-picture Continues playback in a miniature player on top of other apps Player resize button @@ -282,6 +284,9 @@ Delete @string/sort_cancel Pause + Start + Failed + Passed Resume -30 +30 @@ -423,6 +428,7 @@ Enable NSFW on supported providers Subtitle encoding Providers + Provider test Layout Auto TV layout @@ -579,6 +585,8 @@ Audio tracks Video tracks Apply on Restart + Restart + Stop Safe mode on All extensions were turned off due to a crash to help you find the one causing trouble. View crash info @@ -636,4 +644,6 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. + + Hello blank fragment \ No newline at end of file diff --git a/app/src/main/res/xml/settings_providers.xml b/app/src/main/res/xml/settings_providers.xml index a177865b..1ee58faf 100644 --- a/app/src/main/res/xml/settings_providers.xml +++ b/app/src/main/res/xml/settings_providers.xml @@ -1,24 +1,30 @@ + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:icon="@drawable/ic_baseline_language_24" + android:key="@string/provider_lang_key" + android:title="@string/provider_lang_settings" /> + android:icon="@drawable/ic_baseline_play_arrow_24" + android:key="@string/prefer_media_type_key" + android:title="@string/preferred_media_settings" /> + android:icon="@drawable/ic_outline_voice_over_off_24" + android:key="@string/display_sub_key" + android:title="@string/display_subbed_dubbed_settings" /> + android:icon="@drawable/ic_baseline_extension_24" + android:key="@string/enable_nsfw_on_providers_key" + android:summary="@string/apply_on_restart" + android:title="@string/enable_nsfw_on_providers" + app:defaultValue="false" /> + + + \ No newline at end of file From 9d0cce47a67472260e553ba8acd2e4d08fd43cc9 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:40:50 +0000 Subject: [PATCH 231/273] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2e249948..354dc89c 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 @@ -68,12 +68,12 @@ val appLanguages = arrayListOf( Triple("", "español", "es"), Triple("", "فارسی", "fa"), Triple("", "français", "fr"), - Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), Triple("", "हिन्दी", "hi"), Triple("", "hrvatski", "hr"), Triple("", "magyar", "hu"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"), Triple("", "italiano", "it"), + Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), From 789cd14ef6fcb9cfdd5646d79af25f16916cf3d3 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Wed, 15 Feb 2023 21:41:20 +0100 Subject: [PATCH 232/273] remove placeholder --- app/src/main/res/values/strings.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cb9d5508..47517378 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -644,6 +644,4 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. - - Hello blank fragment \ No newline at end of file From 3dd0fc6c8e90e70eb413aba256da5cc4e3ab6f2e Mon Sep 17 00:00:00 2001 From: Lag <> Date: Wed, 15 Feb 2023 22:09:08 +0100 Subject: [PATCH 233/273] add view_test --- app/src/main/res/layout/view_test.xml | 138 ++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 app/src/main/res/layout/view_test.xml diff --git a/app/src/main/res/layout/view_test.xml b/app/src/main/res/layout/view_test.xml new file mode 100644 index 00000000..99300ce4 --- /dev/null +++ b/app/src/main/res/layout/view_test.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From aacd57cb5d2ae860fad11640d0ada1fe3fd55d2d Mon Sep 17 00:00:00 2001 From: Lag <> Date: Thu, 16 Feb 2023 01:15:30 +0100 Subject: [PATCH 234/273] Fixed scrolling up on bottom dialogs and removing stuff from AniList --- .../syncproviders/providers/AniListApi.kt | 53 +++++++++++++----- .../ui/result/ResultFragmentPhone.kt | 2 - .../ui/result/ResultFragmentTv.kt | 5 +- .../layout/bottom_selection_dialog_direct.xml | 54 +++++++++---------- 4 files changed, 69 insertions(+), 45 deletions(-) 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 7d9de43a..0010ce25 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 @@ -759,6 +759,11 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { return data != "" } + /** Used to query a saved MediaItem on the list to get the id for removal */ + data class MediaListItemRoot(@JsonProperty("data") val data: MediaListItem? = null) + data class MediaListItem(@JsonProperty("MediaList") val MediaList: MediaListId? = null) + data class MediaListId(@JsonProperty("id") val id: Long? = null) + private suspend fun postDataAboutId( id: Int, type: AniListStatusType, @@ -766,19 +771,43 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { progress: Int? ): Boolean { val q = - """mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ - aniListStatusString[maxOf( - 0, - type.value - )] - }, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) { - SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) { - id - status - progress - score - } + // Delete item if status type is None + if (type == AniListStatusType.None) { + val userID = getKey(accountId, ANILIST_USER_KEY)?.id ?: return false + // Get list ID for deletion + val idQuery = """ + query MediaList(${'$'}userId: Int = $userID, ${'$'}mediaId: Int = $id) { + MediaList(userId: ${'$'}userId, mediaId: ${'$'}mediaId) { + id + } + } + """ + val response = postApi(idQuery) + val listId = + tryParseJson(response)?.data?.MediaList?.id ?: return false + """ + mutation(${'$'}id: Int = $listId) { + DeleteMediaListEntry(id: ${'$'}id) { + deleted + } + } + """ + } else { + """mutation (${'$'}id: Int = $id, ${'$'}status: MediaListStatus = ${ + aniListStatusString[maxOf( + 0, + type.value + )] + }, ${if (score != null) "${'$'}scoreRaw: Int = ${score * 10}" else ""} , ${if (progress != null) "${'$'}progress: Int = $progress" else ""}) { + SaveMediaListEntry (mediaId: ${'$'}id, status: ${'$'}status, scoreRaw: ${'$'}scoreRaw, progress: ${'$'}progress) { + id + status + progress + score + } }""" + } + val data = postApi(q) return data != "" } 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 b38e1765..2f232995 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 @@ -322,9 +322,7 @@ class ResultFragmentPhone : ResultFragment() { // it?.dismiss() //} builder.setCanceledOnTouchOutside(true) - builder.show() - builder } } 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 2bd8ff0f..71ecb0e9 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 @@ -176,8 +176,7 @@ class ResultFragmentTv : ResultFragment() { 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 @@ -187,9 +186,7 @@ class ResultFragmentTv : ResultFragment() { // it?.dismiss() //} builder.setCanceledOnTouchOutside(true) - builder.show() - builder } } diff --git a/app/src/main/res/layout/bottom_selection_dialog_direct.xml b/app/src/main/res/layout/bottom_selection_dialog_direct.xml index 0d179ebb..cf31ba1f 100644 --- a/app/src/main/res/layout/bottom_selection_dialog_direct.xml +++ b/app/src/main/res/layout/bottom_selection_dialog_direct.xml @@ -1,34 +1,34 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + android:id="@+id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_rowWeight="1" + android:layout_marginTop="20dp" + android:layout_marginBottom="10dp" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:textColor="?attr/textColor" + android:textSize="20sp" + android:textStyle="bold" + tools:text="Test" /> + android:id="@+id/listview1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_rowWeight="1" + android:layout_marginBottom="60dp" + android:nestedScrollingEnabled="true" + android:nextFocusLeft="@id/apply_btt" + android:nextFocusRight="@id/cancel_btt" + android:paddingTop="10dp" + android:requiresFadingEdge="vertical" + tools:listitem="@layout/sort_bottom_single_choice_no_checkmark" /> From b6ac155350cf6d4b070e79276efd6389b5858f05 Mon Sep 17 00:00:00 2001 From: MhmdIbrahim1 <107378571+MhmdIbrahim1@users.noreply.github.com> Date: Fri, 17 Feb 2023 23:42:20 +0200 Subject: [PATCH 235/273] update VideoDownloadService (#377) --- .../services/VideoDownloadService.kt | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt b/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt index be2fe75b..6151a0ed 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/services/VideoDownloadService.kt @@ -1,11 +1,22 @@ package com.lagradost.cloudstream3.services - -import android.app.IntentService +import android.app.Service import android.content.Intent +import android.os.IBinder import com.lagradost.cloudstream3.utils.VideoDownloadManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.launch -class VideoDownloadService : IntentService("VideoDownloadService") { - override fun onHandleIntent(intent: Intent?) { +class VideoDownloadService : Service() { + + private val downloadScope = CoroutineScope(Dispatchers.Default) + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { if (intent != null) { val id = intent.getIntExtra("id", -1) val type = intent.getStringExtra("type") @@ -14,10 +25,36 @@ class VideoDownloadService : IntentService("VideoDownloadService") { "resume" -> VideoDownloadManager.DownloadActionType.Resume "pause" -> VideoDownloadManager.DownloadActionType.Pause "stop" -> VideoDownloadManager.DownloadActionType.Stop - else -> return + else -> return START_NOT_STICKY + } + + downloadScope.launch { + VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) } - VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) } } + + return START_NOT_STICKY } -} \ No newline at end of file + + override fun onDestroy() { + downloadScope.coroutineContext.cancel() + super.onDestroy() + } +} +// override fun onHandleIntent(intent: Intent?) { +// if (intent != null) { +// val id = intent.getIntExtra("id", -1) +// val type = intent.getStringExtra("type") +// if (id != -1 && type != null) { +// val state = when (type) { +// "resume" -> VideoDownloadManager.DownloadActionType.Resume +// "pause" -> VideoDownloadManager.DownloadActionType.Pause +// "stop" -> VideoDownloadManager.DownloadActionType.Stop +// else -> return +// } +// VideoDownloadManager.downloadEvent.invoke(Pair(id, state)) +// } +// } +// } +//} From b4065b69beeb6ab12298aba3ea74583fe5f7372f Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Fri, 17 Feb 2023 23:05:11 +0100 Subject: [PATCH 236/273] Added dropdown indicators Solves #375 --- app/src/main/res/layout/fragment_result.xml | 23 ++++++++++++--------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index afbf735d..a481ed6b 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -129,9 +129,9 @@ + android:paddingBottom="100dp"> @@ -516,8 +515,8 @@ android:visibility="gone" /> نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق - مُوصي به + مُوصى به تم تحميل %s إختيار ملف تحميل من الانترنت @@ -543,4 +543,16 @@ تلفزيون أندرويد مدة التقديم عنما يكون المشغل مرئيا مدة التقديم- المشغل المرئي + فشل + نجح + إختبار المزود + إعادة التشغيل + سجل + بَدأ + إيقاف + تحديث العروض التي تم الاشتراك فيها + إلغاء الاشتراك من %s + تم إصدار الحلقة %d! + مشترك + مشترك في %s \ 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 a78da8a4..966cd7d9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -535,4 +535,16 @@ Zobrazený přehrávač - doba hledání Android TV Množství vyhledávané doby při zobrazeném přehrávači + Protokol + Test poskytovatele + Neúspěšné + Úspěšné + Restart + Spustit + Zastavit + Aktualizace odebíraných pořadů + Přihlášeno k odběru %s + Odhlášen odběr od %s + Byla vydána epizoda %d! + Odebíráno \ 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 e190aa9c..f6583c20 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -53,7 +53,7 @@ Abgebrochen Geplant Nichts - Erneut anschauen + Erneut schauen Film abspielen Livestream abspielen Torrent streamen @@ -212,7 +212,7 @@ Keine Untertitel Standard Frei - Benutzt + Belegt App Filme TV-Serien @@ -284,7 +284,7 @@ Strecken Vergrößern Haftungsausschluss - General + Allgemein Zufalls-Button Zufallsbutton auf der Startseite anzeigen Anbieter-Sprachen @@ -460,11 +460,11 @@ Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories. Einrichtungsvorgang wiederholen APK-Installer - Einige Telefone unterstützen das neue Installationsprogramm für Pakete nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen. + Einige Telefone unterstützen den neuen Package-Installer nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen. %s %d%s Links App-Updates - Back-Up + Sicherung Erweiterungen Wartung Cache @@ -506,4 +506,16 @@ Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln. Datei für abgesicherten Modus gefunden! \nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird. + Player ausgeblendet - Betrag zum vor- und zurückspulen + Der Betrag, welcher verwendet wird, wenn der Player eingeblendet ist + Der Betrag, welcher verwendet wird, wenn der Player ausgeblendet ist + Android-TV + Player eingeblendet - Betrag zum vor- und zurückspulen + Fehlgeschlagen + Erfolgreich + Anbieter-Test + Stopp + Log + Start + Neustarten \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0d0b7fb2..5e9dafd8 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -150,7 +150,7 @@ Επεισόδια %d-%d %d %s - Κ + Σ E Δεν βρέθηκαν επεισόδια Διαγραφή αρχείου diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 8366b294..2040169b 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -511,4 +511,16 @@ Android TV La cantidad de búsqueda utilizada cuando la jugadora es visible La cantidad de búsqueda utilizada cuando el jugador está oculto + Parar + Falló + Registro + Empezar + Aprobado + Prueba del proveedor + Reiniciar + Suscrito + Suscrito a %s + Darse de baja de %s + Actualizando los programas suscritos + ¡Episodio %d publicado! \ 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 f0e112a8..18255b3b 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -6,12 +6,12 @@ Téléchargements Paramètres Rechercher… - Miniature + Affiche Aucune Donnée Plus d\'options Retour Épisode suivant - Miniature + Affiche Genres Partager Ouvrir dans le navigateur @@ -29,7 +29,7 @@ Sous-titres Réessayer la connection… Retour - Miniature de l\'Épisode + Affiche de l\'épisode Lire l\'Épisode Télécharger @@ -51,10 +51,10 @@ Désactiver le rapport de bug automatique Plus d\'informations Cacher - Poster principal + Affiche principale Lecture - Info - Suivant Aléatoire + Infos + Aléatoire suivant Changer le fournisseur Filtrer les marques-pages Marque-pages @@ -211,7 +211,7 @@ Arrière plan Source Aléatoire - À venir … + Bientôt disponible… Image de l\'affiche %s Connecté Définir le statut de visionage @@ -490,4 +490,22 @@ L\'application sera mise à jour dès la fin de la session Plugin Téléchargé Retirer de la vue + Bibliothèque + Navigateur + Trier + Note (basse à haute) + Note (haut à bas) + Alphabétique (A à Z) + On dirait que votre bibliothèque est vide :( +\nConnectez-vous à un compte ou ajoutez des séries à votre bibliothèque locale + Il semble que cette liste soit vide, essayez d\'en choisir une autre + Android TV + Trié par + Alphabétique (Z à A) + Sélectionnez la bibliothèque + Ouvrir avec + Mis à jour (Nouveau vers ancien) + Mis à jour (ancien vers nouveau) + Fichier du mode sans échec trouvé ! +\nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé. \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 0f3e36bc..926c7f57 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -531,4 +531,21 @@ Čini se da je ova lista prazna, pokušajte se prebaciti na drugu Pronađena datoteka sigurnog načina rada! \nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni. + Prikazan player- iznos preskakanja + Količina preskakanja koja se koristi kada je player vidljiv + Player skriven - Količina preskakanja + Količina preskakanja koja se koristi kada je player skriven + Android TV + Prošlo + Restart + Log + Početak + Neuspješno + Stop + Test pružatelja usluga + Ažuriram pretplaćene serije + Epizoda %d izbačena! + Pretplaćeno + Pretplaćen na %s + Otkazana pretplata sa %s \ 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 d54e4fa9..46d61e44 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -35,7 +35,7 @@ Skip Loading Loading… Sedang Menonton - Tertahan + Tertunda Selesai Dihentikan Rencana untuk Menonton @@ -387,7 +387,7 @@ %d %s 17+ Lainnya - Vidio + Video Duplikasi Website Duplikasi website yang telah ada, dengan alamat berbeda Tautan @@ -395,7 +395,7 @@ Cadangkan Fitur Tambahan Putar di CloudStream - Sembunyikan kualitas vidio terpilih di pencarian + Sembunyikan kualitas video terpilih di pencarian %s %d%s Siaran langsung Hapus Website @@ -444,7 +444,7 @@ Peringkat: %s Pembuat Bahasa - Pemutar vidio utama + Pemutar video utama Pemutar Bawaan VLC MPV @@ -475,7 +475,7 @@ Hapus teks tertutup dari subtitel Hapus karakter sampah dari subtitel Audio Trek - Vidio Trek + Video Trek Dukungan Daftar putar HLS Penginstal APK @@ -529,4 +529,21 @@ Yahh daftar ini kosong, coba ganti ke yang lain Mode aman file ditemukan! \nTidak memuat ekstensi pada startup sampai berkas dihapus. + Sembunyikan Pemutaran - Geser + Pemutar terlihat - Geser + Geser untuk menghilangkan + Geser untuk menghilangkan + Android TV + Log + Berhasil + Tes provider + Berhenti + Mulai + Mulai lagi + Gagal + Memperbarui acara langganan + Berlangganan + Berlangganan ke %s + Berhenti berlangganan di %s + Episode %d telah rilis! \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 9dbc627f..89f6b4ee 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -528,4 +528,16 @@ Sembra che questa lista sia vuota, prova a passare a un\'altra File \"safe mode\" trovato! \nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso. + Quantità di ricerca usata quando il player è nascosto + TV Android + Quantità di ricerca usata quando il player è visibile + Player visibile - Quantità di ricerca + Player nascosto - Quantità di ricerca + Registro + Avvia + Test del provider + Riavvia + Ferma + Superato + Fallito \ No newline at end of file diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml new file mode 100644 index 00000000..a3d1d434 --- /dev/null +++ b/app/src/main/res/values-ja/strings.xml @@ -0,0 +1,185 @@ + + + %d分 + ダウンロード + 検索 + 設定 + シェア + 映画 + ホーム + ライブラリ + 再生 + %d日 %d時間%d分 + %d時間%d分 + 検索… + ダウンロード + 情報 + シーズン + 予告編 + シリーズ + エピソード + 再生速度 (%.2fx) + 次のエピソード + 適用 + アカウント + カートゥーン + TVシリーズ + トレント + ドキュメンタリー + OVA + アジアドラマ + ライブ配信 + 映画 + その他 + カートゥーン + トレント + ドキュメンタリー + アジアドラマ + ライブ配信 + NSFW + キャンセル + アニメ + ロック + ソース + NSFW + 履歴を削除 + 視聴中コンテンツ + 全般 + 動画 + プレーヤー + 懐う + 予告編を再生 + エピソード + 視聴 + ジャンル + 映画を再生 + 字幕 + CloudStream + CloudStreamで再生 + ブラウザ + 完成 + 放置 + 保留 + ローディング… + ブラウザで開く + シーズン + 残り +\n%d分 + 再生エピソード + ダウンロード済 + バックアップ + ソース + 履歴 + ポスター + なし + コピー + 閉じる + 保存 + 消去 + %sエピ%d + 出演者:%s + ポスター + エピソードポスター + 主要ポスター + 次のランダム + 戻り + 視聴率 %.1f + 新しいアップデートを発見! +\n%s -> %s + %d分 + %sを検索… + ソース + ろくごうきじ + 接続を再試行… + 戻り + 削除 + 詳細情報 + 閉じる + アップデート・バックアップ + アプリ言語 + GitHub(ギットハブ) + -30 + +30 + 免責 + 拡張機能 + アプリ更新 + 提供者 + 字幕 + 特徴 + デフォルト + 自動 + 任意 + 拡張機能 + リンク + Android TV + ログイン + ログアウト + 最大 + 最小 + なし + + 18+ + + で開く + エピソード + 時間 + 概要 + サイト + 使用 + アプリ + 詳細情報 + 削除 + ピクチャーインピクチャー + 字幕 + 情報 + 一時停止 + 再生エピソード + 削除 + 開始 + 状態 + + 再開 + 失敗 + 合格 + 空き + 完成 + 進行中 + デフォルト + ウェブブラウザ + VLC + MPV + 言語 + 作成者 + サイズ + 状態 + バージョン + 視聴率 %s + 視聴率 + デフォルト + ダウンロード失敗 + ダウンロード開始 + ダウンロード完了 + ダウンロード終了 + ストリーム + アップデート開始 + シーズンなし + 字幕なし + アスペクト比 + ロードをスキップする + その他のオプション + データなし + ダウンロード中 + ブックマーク + 内部記憶装置 + ダウンロードが一時停止 + メタデータはこのサイトでは提供されません。メタデータがサイト上に存在しない場合、ビデオの読み込みに失敗します。 + 記述 + Logcat 🐈を表示 + ログ + 検索 + Discordに参加 + アップデート + アップデートを確認 + 作品名 + アプリのアップデートをインストール中… + \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index efe0a1d8..c36459b7 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,3 +1,128 @@ - \ No newline at end of file + %sಎಪಿ%d + ಕ್ಯಾಸ್ಟ್:%s + ಹಿಂದೆ ಹೋಗು + ಫಿಲ್ಲರ್ + ಹುಡುಕು + ಡೌನ್ಲೋಡ್ + ಫಾಂಟ್ + ಪೂರೈಕೆದಾರರನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ + ಪ್ರಕಾರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ + ಯಾವುದೇ ಬೆನೆನ್ಸ್ ನೀಡಿಲ್ಲ + ಸ್ವಯಂ-ಆಯ್ಕೆ ಭಾಷೆ + ಹೆಚ್ಚಿನ ಮಾಹಿತಿ + \@ಸ್ಟ್ರಿಂಗ್/ಹೋಮ್_ಪ್ಲೇ + ಈ ಪೂರೈಕೆದಾರರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಲು VPN ಬೇಕಾಗಬಹುದು + ಕಪ್ಪು ಗಡಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ + ಸಂಚಿಕೆ%d ಬಿಡುಗಡೆಯಾಗಲಿದೆ + %dh %dm + ಪೋಸ್ಟರ್ + ಪೋಸ್ಟರ್ + ಸಂಚಿಕೆ ಪೋಸ್ಟರ್ + ಮೇನ್ ಪೋಸ್ಟರ್ + ಅಪ್ಡೇಟ್ ಪ್ರಾರಂಭವಾಗಿದೆ + ಲೋಡಿಂಗ್ ಲಿಂಕ್ ಎರರ್ ಬಂದಿದೆ + ಇಂಟರ್ನಲ್ ಸ್ಟೋರೇಜ್ + ಡಬ್ + ಸಬ್ + ಸ್ವಯಂಚಾಲಿತ ದೋಷ ವರದಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ + ಹೈಡ್ + ಪ್ಲೇ + ಮಾಹಿತಿ + ಸೆಟ್ ವಾಚ್ ಸ್ಟೇಟಸ್ + ಅನ್ವಯಿಸು + ರದ್ದುಮಾಡು + ಸಬ್ ಟೈಟಲ್ಸ್ ಎಲೆವಷನ್ + ಫಾಂಟ್ ಸೈಜ್ + ಸಬ್ ಟೈಟಲ್ಸ್ ಭಾಷೆ + ತೆಗೆದುಹಾಕಿ + ಈ ಪೂರೈಕೆದಾರರು ಟೊರೆಂಟ್ ಆಗಿದೆ, VPN ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ + ಯಾವುದೇ ಪ್ಲಾಟ್ ಕಂಡುಬಂದಿಲ್ಲ + ಲಾಗ್‌ಕ್ಯಾಟ್ 🐈 ತೋರಿಸಿ + ಲಾಗ್ + ಚಿತ್ರದಲ್ಲಿ-ಚಿತ್ರದಲ್ಲಿ + ಪ್ಲೇಯರ್ ಮರುಗಾತ್ರಗೊಳಿಸಿ ಬಟನ್ + ಸಬ್ ಟೈಟಲ್ಸ್ + ಪ್ಲೇಯರ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು + ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್ + ಹಿಂದೆ ಹೋಗು + ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಿ + ಬುಕ್‌ಮಾರ್ಕ್‌ + ಬ್ಯಾಕ್ ಗ್ರೌಂಡ್ ಕಲರ್ + %d ಡೇವ್‌ಗಳಿಗೆ ಬೆನೆನೆಸ್ ನೀಡಲಾಗಿದೆ + ಡೀಫಾಲ್ಟ್‌ಗೆ ಮರುಹೊಂದಿಸಲು ಹಿಡಿದುಕೊಳ್ಳಿ + ಸೈಟ್‌ನಿಂದ ಮೆಟಾಡೇಟಾವನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ, ಅದು ಸೈಟ್‌ನಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲದಿದ್ದರೆ ವೀಡಿಯೊ ಲೋಡಿಂಗ್ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ. + ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲೆ ಚಿಕಣಿ ಪ್ಲೇಯರ್‌ನಲ್ಲಿ ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಮುಂದುವರಿಸುತ್ತದೆ + ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ + ರೇಟೆಡ್:%.1f + ತೆಗೆದುಹಾಕಿ + ಡೌನ್‌ಲೋಡ್ ಅನ್ನು ಪುನರಾರಂಭಿಸಿ + ಕ್ಲೋಸ್ + ಕ್ಲಿಯರ್ + ಸೇವ್ + ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್ + ಫೈಲ್ ಪ್ಲೇ + ಟೆಕ್ಸ್ಟ್ ಕಲರ್ + ಔಟ್ ಲೈನ್ ಕಲರ್ + ವಿಂಡೋ ಕಲರ್ + ಎಡ್ಜ್ ಟೈಪ್ + ಪ್ರೊವೈಡರ್ ಬದಲಾಯಿಸಿ + %dಮಿನ + ವಿವರಣೆ + ಸ್ಪೀಡ್(%.2fx) + ಹೋಂ + ಸಬ್ ಟೈಟಲ್ಸ್ + ಸೆಟ್ಟಿಂಗ್ಸ್ + ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ + ಹುಡುಕು… + ಚಲನಚಿತ್ರವನ್ನು ಪ್ಲೇ ಮಾಡಿ + ಪ್ರಿವ್ಯೂ ಹಿನ್ನೆಲೆ + ಮುಂದಿನ ಸಂಚಿಕೆ + ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ + ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ + ಸ್ಟ್ರೀಮ್ + ಶೇರ್ + ಫೈಲ್ ಅಳಿಸಿ + ಹೆಚ್ಚಿನ ಮಾಹಿತಿ + ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ +\n%s-%s + ಲೋಡಿಂಗ್… + ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ + ಲೈವ್‌ಸ್ಟ್ರೀಮ್ ಪ್ಲೇ ಮಾಡಿ + ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ ಇದರೊಂದಿಗೆ ಪ್ಲೇ ಮಾಡಿ + ವೀಕ್ಷಿಸಲು ಯೋಜನೆ + ಸಂಚಿಕೆಯನ್ನು ಪ್ಲೇ ಮಾಡಿ + ಕಂಟಿನ್ಯೂ ವಾಟಚಿಂಗ್ + ಯಾವುದೇ ವಿವರಣೆ ಕಂಡುಬಂದಿಲ್ಲ + ಸ್ಟ್ರೀಮ್ ಟೊರೆಂಟ್ + ಡೌನ್‌ಲೋಡ್ + ಕಾಪಿ + ನೋ ಡೇಟಾ + ಪ್ಲೇಯರ್ ಸ್ಪೀಡ್ + %d %dh %dm + ಹುಡುಕು %s… + ಹೆಚ್ಚಿನ ಆಯ್ಕೆ + ಫಾಂಟ್‌ಗಳನ್ನು ಇರಿಸುವ ಮೂಲಕ ಆಮದು ಮಾಡಿ %s + %dm + ಪ್ರಕಾರಗಳು + ಬ್ರೌಸರ್ ತೆರೆಯಿರಿ + ಆನ್-ಹೋಲ್ಡ್ + ನನ್ + ಸಂಪರ್ಕವನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ… + ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ವಿಫಲವಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ಮುಗಿದಿದೆ + ಬ್ರೌಸರ್ + ಸ್ಕಿಪ್ ಲೋಡಿಂಗ್ + ವಾಚಿಂಗ್ + ಪೂರ್ಣಗೊಂಡಿದೆ + ಕೈಬಿಡಲಾಯಿತು + ಪುನಃ ವೀಕ್ಷಿಸುತ್ತಿದೆ + ಟ್ರೈಲರ್ ಪ್ಲೇ ಮಾಡಿ + ಮೂಲಗಳು + ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ + ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ + ಮುಂದಿನ ರಾಂಡಮ್ + \ 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 c709f124..411f0b45 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -373,7 +373,7 @@ Pomiń setup Dostosuj wygląd aplikacji do urządzenia Zgłaszanie błędów - Co chciałbyś obejrzeć\? + Co chciałbyś obejrzeć Gotowe Rozszerzenia Dodaj repozytorium @@ -509,4 +509,9 @@ Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną Znaleziono plik trybu bezpiecznego. \nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty. + Używana ilość przewijania, gdy widoczny jest odtwarzacz + Ukryty odtwarzacz - ilość przewijania + Android TV + Pokazany odtwarzacz — ilość przewijania + Używana ilość przewijania, gdy ukryty jest odtwarzacz \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 982546bc..42d9b7c8 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Poster Episod Poster Principal Următorul la Întâmplare @@ -142,7 +142,7 @@ Fișier de rezervă încărcat Imposibilitatea de a restaura datele din %s Date stocate - Permisiuni de arhivare lipsă, vă rugăm să încercați din nou + Permisiunea de arhivare lipșe, vă rugăm să încercați din nou. Eroare de backup %s Căutare Conturi și credite @@ -154,7 +154,7 @@ Nu trimiteți niciun fel de date Afișează etichetele [filler] pentru anime Arată trailerul - Arată posterele de la Kitsu + Arată afișele de la Kitsu Afișați actualizările aplicației Căutați automat noi actualizări la pornire Actualizați la prerelease @@ -384,4 +384,8 @@ Începe următorul episod când se termină episodul curent Ascundeți calitatea video selectată în rezultatele căutării Redare Livestream + Librărie + Log + Browser + Joacă cu CloudStream \ 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 6e9fb394..2812667a 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -506,4 +506,16 @@ Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем Удалять лишнее из субтитров + Местоположение ползунка, когда игрок скрыт + Android TV + Второго планa + Смешанный опенинг + Смешанный конец + Тест провайдер + Журнал + Запустить + Выполнено + Неудачный + Прекратить + Перезапустить \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 97039233..66d8ada9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -17,7 +17,7 @@ %dd %dh %dm %dm %d min - \@string/result_poster_img_des + Plagát Plagát epizódy Hlavný plagát Prehrať s CloudStream diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 871e0a28..5330d3ec 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -507,4 +507,20 @@ Файл безпечного режиму знайдено! \nРозширеня не завантажуються під час запуску, доки файл не буде видалено. Android TV + Плеєр сховано - обсяг пошуку + Плеєр показано - обсяг пошуку + Обсяг пошуку, який використовується, коли плеєр видимий + Обсяг пошуку, який використовується, коли гравець прихований + Не вдалося + Пройдено + Перезапуск + Журнал + Старт + Стоп + Тест постачальника + Оновлення підписаних шоу + Підписано + Підписано на %s + Відписатися від %s + Епізод %d випущено! \ 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 a8341d46..8a10208a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -19,7 +19,7 @@ %dm 封面 - \@string/result_poster_img_des + 封面 劇集封面 主封面 隨機下一個 @@ -533,4 +533,5 @@ 預設 外觀 功能 + 瀏覽器 \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index c57e3ca1..9e2d6137 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -554,4 +554,21 @@ 看来您的库是空的 :( \n登录库账户或添加节目到您的本地库 看来此列表是空的,请尝试切换到另一个 + 播放器显示 - 快进快退秒数 + 播放器可见时使用的快进快退秒数 + 播放器隐藏 - 快进快退秒数 + 播放器隐藏时使用的快进快退秒数 + Android TV + 失败 + 片源测试 + 重启 + 停止 + 正在更新订阅节目 + 已订阅 + 已订阅 %s + 已取消订阅 %s + 开始 + 第 %d 集已发布! + 成功 + 日志 \ No newline at end of file From 51137701f2c6228660720fba94d9dfc53cefb582 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:43:35 +0100 Subject: [PATCH 240/273] add proxy to raw.githubusercontent.com (#368) --- .../lagradost/cloudstream3/MainActivity.kt | 34 ++++++++++++++++++ .../cloudstream3/plugins/RepositoryManager.kt | 30 ++++++++++++---- .../ui/settings/SettingsGeneral.kt | 13 ++++--- .../lagradost/cloudstream3/utils/AppUtils.kt | 8 ++++- app/src/main/res/values/strings.xml | 6 ++++ app/src/main/res/xml/settins_general.xml | 36 ++++++++++++------- 6 files changed, 102 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 28419e7a..e626dcd6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -32,7 +32,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.google.android.gms.cast.framework.* import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.navigationrail.NavigationRailView +import com.google.android.material.snackbar.Snackbar import com.jaredrummler.android.colorpicker.ColorPickerDialogListener +import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings @@ -79,6 +81,7 @@ import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable +import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadResult @@ -86,6 +89,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching @@ -717,6 +721,28 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) + // Automatically enable jsdelivr if cant connect to raw.githubusercontent.com + if (this.getKey(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) { + main { + if (checkGithubConnectivity()) { + this.setKey(getString(R.string.jsdelivr_proxy_key), false) + } else { + this.setKey(getString(R.string.jsdelivr_proxy_key), true) + val parentView: View = findViewById(android.R.id.content) + Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG).let { snackbar -> + snackbar.setAction(R.string.revert) { + setKey(getString(R.string.jsdelivr_proxy_key), false) + } + snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) + snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) + snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) + snackbar.show() + } + } + + } + } + if (PluginManager.checkSafeModeFile()) { normalSafeApiCall { @@ -1090,4 +1116,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // } } + + suspend fun checkGithubConnectivity(): Boolean { + return try { + app.get("https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", timeout = 5).text.trim() == "ok" + } catch (t: Throwable) { + false + } + } } 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 e77b2d54..742bf308 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/RepositoryManager.kt @@ -2,8 +2,10 @@ package com.lagradost.cloudstream3.plugins import android.content.Context import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -71,6 +73,15 @@ object RepositoryManager { val PREBUILT_REPOSITORIES: Array by lazy { getKey("PREBUILT_REPOSITORIES") ?: emptyArray() } + val GH_REGEX = Regex("^https://raw.githubusercontent.com/([A-Za-z0-9-]+)/([A-Za-z0-9_.-]+)/(.*)$") + + /* Convert raw.githubusercontent.com urls to cdn.jsdelivr.net if enabled in settings */ + fun convertRawGitUrl(url: String): String { + if (getKey(context!!.getString(R.string.jsdelivr_proxy_key)) != true) return url + val match = GH_REGEX.find(url) ?: return url + val (user, repo, rest) = match.destructured + return "https://cdn.jsdelivr.net/gh/$user/$repo@$rest" + } suspend fun parseRepoUrl(url: String): String? { val fixedUrl = url.trim() @@ -84,10 +95,15 @@ object RepositoryManager { } } else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) { suspendSafeApiCall { - app.get("https://l.cloudstream.cf/${fixedUrl}").let { - return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url - else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 -> - return@let2 if (it2.isSuccessful) it2.url else null + app.get("https://l.cloudstream.cf/${fixedUrl}", allowRedirects = false).let { + it.headers["Location"]?.let { url -> + return@suspendSafeApiCall if (!url.startsWith("https://cutt.ly/branded-domains")) url + else null + } + app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 -> + it2.headers["Location"]?.let { url -> + return@suspendSafeApiCall if (url.startsWith("https://cutt.ly/404")) url else null + } } } } @@ -97,14 +113,14 @@ object RepositoryManager { suspend fun parseRepository(url: String): Repository? { return suspendSafeApiCall { // Take manifestVersion and such into account later - app.get(url).parsedSafe() + app.get(convertRawGitUrl(url)).parsedSafe() } } private suspend fun parsePlugins(pluginUrls: String): List { // Take manifestVersion and such into account later return try { - val response = app.get(pluginUrls) + val response = app.get(convertRawGitUrl(pluginUrls)) // Normal parsed function not working? // return response.parsedSafe() tryParseJson>(response.text)?.toList() ?: emptyList() @@ -139,7 +155,7 @@ object RepositoryManager { } file.createNewFile() - val body = app.get(pluginUrl).okhttpResponse.body + val body = app.get(convertRawGitUrl(pluginUrl)).okhttpResponse.body write(body.byteStream(), file.outputStream()) file } 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 354dc89c..c5a11cce 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 @@ -82,7 +82,7 @@ val appLanguages = arrayListOf( Triple("", "norsk bokmål", "no"), Triple("", "polski", "pl"), Triple("\uD83C\uDDF5\uD83C\uDDF9", "português", "pt"), - Triple("🦍", "mmmm... monke", "qt"), + Triple("\uD83E\uDD8D", "mmmm... monke", "qt"), Triple("", "română", "ro"), Triple("", "русский", "ru"), Triple("", "slovenčina", "sk"), @@ -97,7 +97,7 @@ val appLanguages = arrayListOf( Triple("", "中文", "zh"), Triple("\uD83C\uDDF9\uD83C\uDDFC", "文言", "zh-rTW"), /* end language list */ -).sortedBy { it.second?.toLowerCase() } //ye, we go alphabetical, so ppl don't put their lang on top +).sortedBy { it.second.lowercase() } //ye, we go alphabetical, so ppl don't put their lang on top class SettingsGeneral : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -157,9 +157,6 @@ class SettingsGeneral : PreferenceFragmentCompat() { getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> val tempLangs = appLanguages.toMutableList() - //if (beneneCount > 100) { - // tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo")) - //} val current = getCurrentLocale(pref.context) val languageCodes = tempLangs.map { (_, _, iso) -> iso } val languageNames = tempLangs.map { (emoji, name, iso) -> @@ -316,6 +313,12 @@ class SettingsGeneral : PreferenceFragmentCompat() { } ?: emptyList() } + settingsManager.edit().putBoolean(getString(R.string.jsdelivr_proxy_key), getKey(getString(R.string.jsdelivr_proxy_key), false) ?: false).apply() + getPref(R.string.jsdelivr_proxy_key)?.setOnPreferenceChangeListener { _, newValue -> + setKey(getString(R.string.jsdelivr_proxy_key), newValue) + return@setOnPreferenceChangeListener true + } + getPref(R.string.download_path_key)?.setOnPreferenceClickListener { val dirs = getDownloadDirs() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 860144ee..205f0a6b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -491,6 +491,12 @@ object AppUtils { } } + fun Context.isNetworkAvailable(): Boolean { + val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = manager.activeNetworkInfo + return activeNetworkInfo != null && activeNetworkInfo.isConnected || manager.allNetworkInfo?.any { it.isConnected } ?: false + } + fun splitQuery(url: URL): Map { val queryPairs: MutableMap = LinkedHashMap() val query: String = url.query @@ -815,4 +821,4 @@ object AppUtils { } return currentAudioFocusRequest } -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0858fdfa..2d46a70d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,6 +43,7 @@ random_button_key provider_lang_key dns_key + jsdelivr_proxy_key download_path_key Cloudstream app_layout_key @@ -378,6 +379,9 @@ Causes problems if set too high on devices with low storage space, such as Android TV. DNS over HTTPS Useful for bypassing ISP blocks + raw.githubusercontent.com Proxy + Failed to reach GitHub, enabling jsdelivr proxy. + Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -405,6 +409,7 @@ 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 Backup @@ -644,6 +649,7 @@ Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one Safe mode file found!\nNot loading any extensions on startup until file is removed. + Revert Updating subscribed shows Subscribed Subscribed to %s diff --git a/app/src/main/res/xml/settins_general.xml b/app/src/main/res/xml/settins_general.xml index 726f3fd0..c4900bca 100644 --- a/app/src/main/res/xml/settins_general.xml +++ b/app/src/main/res/xml/settins_general.xml @@ -6,18 +6,6 @@ android:title="@string/app_language" android:icon="@drawable/ic_baseline_language_24" /> - - - - + + + + + + + + + + From 1da6a925692de08cb8e8ec00d93d1e5d1f76b8c6 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:07:04 +0000 Subject: [PATCH 241/273] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 1 + 1 file changed, 1 insertion(+) 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 c5a11cce..078419e2 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 @@ -74,6 +74,7 @@ val appLanguages = arrayListOf( Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"), Triple("", "italiano", "it"), Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), + Triple("", "日本語 (にほんご)", "ja"), Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), From aeab423d2929d1de6dd2681592e2e832f261a935 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 24 Feb 2023 18:47:54 +0100 Subject: [PATCH 242/273] Excluded the referer header when empty --- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 4772a7f1..cd384c6f 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 @@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorUri import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import java.io.File +import java.time.Duration import javax.net.ssl.HttpsURLConnection import javax.net.ssl.SSLContext import javax.net.ssl.SSLSession @@ -535,15 +536,16 @@ class CS3IPlayer : IPlayer { OkHttpDataSource.Factory(client).setUserAgent(USER_AGENT) } + // Do no include empty referer, if the provider wants those they can use the header map. + val refererMap = if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) val headers = mapOf( - "referer" to link.referer, "accept" to "*/*", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", "sec-ch-ua-mobile" to "?0", "sec-fetch-user" to "?1", "sec-fetch-mode" to "navigate", "sec-fetch-dest" to "video" - ) + link.headers // Adds the headers from the provider, e.g Authorization + ) + refererMap + link.headers // Adds the headers from the provider, e.g Authorization return source.apply { setDefaultRequestProperties(headers) From f722785a379547fb9f28893e0661eeff186b0a3d Mon Sep 17 00:00:00 2001 From: Hexated <37908684+hexated@users.noreply.github.com> Date: Sat, 25 Feb 2023 01:49:53 +0700 Subject: [PATCH 243/273] fixed Linkbox (#390) --- .../cloudstream3/extractors/Linkbox.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt index c28a8900..6a4945bb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Linkbox.kt @@ -18,31 +18,36 @@ open class Linkbox : ExtractorApi() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val id = Regex("""(/file/|id=)(\S+)[&/?]""").find(url)?.groupValues?.get(2) - app.get("$mainUrl/api/open/get_url?itemId=$id", referer=url).parsedSafe()?.data?.rList?.map { link -> - callback.invoke( - ExtractorLink( - name, - name, - link.url, - url, - getQualityFromName(link.resolution) + val id = Regex("""(?:/f/|/file/|\?id=)(\w+)""").find(url)?.groupValues?.get(1) + app.get("$mainUrl/api/file/detail?itemId=$id", referer = url) + .parsedSafe()?.data?.itemInfo?.resolutionList?.map { link -> + callback.invoke( + ExtractorLink( + name, + name, + link.url ?: return@map null, + url, + getQualityFromName(link.resolution) + ) ) - ) - } + } } - data class RList( - @JsonProperty("url") val url: String, - @JsonProperty("resolution") val resolution: String?, + data class Resolutions( + @JsonProperty("url") val url: String? = null, + @JsonProperty("resolution") val resolution: String? = null, + ) + + data class ItemInfo( + @JsonProperty("resolutionList") val resolutionList: ArrayList? = arrayListOf(), ) data class Data( - @JsonProperty("rList") val rList: List?, + @JsonProperty("itemInfo") val itemInfo: ItemInfo? = null, ) data class Responses( - @JsonProperty("data") val data: Data?, + @JsonProperty("data") val data: Data? = null, ) } \ No newline at end of file From 2926dc6c8eea53a35006b4188f9a03a9a9bb5216 Mon Sep 17 00:00:00 2001 From: Allen Baby <64322605+allenbaby@users.noreply.github.com> Date: Sat, 25 Feb 2023 00:21:03 +0530 Subject: [PATCH 244/273] Issue #376: Added new feature for separate watch quality on mobile data. (#391) * Issue #376: Added new feature for separate watch quality on mobile data. --- .../cloudstream3/ui/player/CS3IPlayer.kt | 3 ++- .../ui/player/FullScreenPlayer.kt | 4 ++-- .../ui/settings/SettingsPlayer.kt | 24 +++++++++++++++++++ .../lagradost/cloudstream3/utils/AppUtils.kt | 7 +++++- app/src/main/res/values/strings.xml | 6 +++-- app/src/main/res/xml/settings_player.xml | 4 ++++ 6 files changed, 42 insertions(+), 6 deletions(-) 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 cd384c6f..782e3fa4 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 @@ -35,6 +35,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.ui.subtitles.SaveCaptionStyle import com.lagradost.cloudstream3.utils.EpisodeSkip +import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLinkPlayList import com.lagradost.cloudstream3.utils.ExtractorUri @@ -849,7 +850,7 @@ class CS3IPlayer : IPlayer { Log.i(TAG, "loadExo") val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) val maxVideoHeight = settingsManager.getInt( - context.getString(com.lagradost.cloudstream3.R.string.quality_pref_key), + context.getString(if (context.isUsingMobileData()) com.lagradost.cloudstream3.R.string.quality_pref_mobile_data_key else com.lagradost.cloudstream3.R.string.quality_pref_key), Int.MAX_VALUE ) 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 8d28fd9d..d1b2814d 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 @@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe @@ -1246,9 +1247,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() { ctx.getString(R.string.double_tap_pause_enabled_key), false ) - currentPrefQuality = settingsManager.getInt( - ctx.getString(R.string.quality_pref_key), + ctx.getString(if (ctx.isUsingMobileData()) R.string.quality_pref_mobile_data_key else R.string.quality_pref_key), currentPrefQuality ) // useSystemBrightness = 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 33d41934..e10a5a1a 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 @@ -113,6 +113,30 @@ class SettingsPlayer : PreferenceFragmentCompat() { return@setOnPreferenceClickListener true } + getPref(R.string.quality_pref_mobile_data_key)?.setOnPreferenceClickListener { + val prefValues = Qualities.values().map { it.value }.reversed().toMutableList() + prefValues.remove(Qualities.Unknown.value) + + val prefNames = prefValues.map { Qualities.getStringByInt(it) } + + val currentQuality = + settingsManager.getInt( + getString(R.string.quality_pref_mobile_data_key), + Qualities.values().last().value + ) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentQuality), + getString(R.string.watch_quality_pref_data), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.quality_pref_mobile_data_key), prefValues[it]) + .apply() + } + 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) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index 205f0a6b..a76b62fd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -776,8 +776,13 @@ object AppUtils { return networkInfo.any { conManager.getNetworkCapabilities(it) ?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true + } && + !networkInfo.any { + conManager.getNetworkCapabilities(it) + ?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true + } } - } + private fun Activity?.cacheClass(clazz: String?) { clazz?.let { c -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2d46a70d..49380b5e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ test_providers_key subtitle_settings_chromecast_key quality_pref_key + quality_pref_mobile_data_key player_pref_key prefer_limit_title_key prefer_limit_title_rez_key @@ -364,7 +365,8 @@ Don\'t show again Skip this Update Update - Preferred watch quality + Preferred watch quality (WiFi) + Preferred watch quality (Mobile Data) Video player title max chars Video player resolution Video buffer size @@ -655,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - \ No newline at end of file + diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml index 0e5bd84f..2d2905ea 100644 --- a/app/src/main/res/xml/settings_player.xml +++ b/app/src/main/res/xml/settings_player.xml @@ -15,6 +15,10 @@ android:icon="@drawable/ic_baseline_hd_24" android:key="@string/quality_pref_key" android:title="@string/watch_quality_pref" /> + Date: Sat, 25 Feb 2023 21:18:48 +0000 Subject: [PATCH 245/273] Added some extractors mirrors and added Vido Extractor (#393) * Added some mirrors and fixed some extractors * Fixed Vido extractor (for MesFilms and Wiflix) --- .../cloudstream3/extractors/DoodExtractor.kt | 3 ++ .../cloudstream3/extractors/Evolaod.kt | 25 ++------------ .../cloudstream3/extractors/Filesim.kt | 5 +++ .../cloudstream3/extractors/StreamSB.kt | 4 +++ .../cloudstream3/extractors/Uqload.kt | 26 ++++---------- .../lagradost/cloudstream3/extractors/Vido.kt | 34 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 5 +++ 7 files changed, 61 insertions(+), 41 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt index 7ec1fb22..0d94eb08 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/DoodExtractor.kt @@ -38,6 +38,9 @@ class DoodWsExtractor : DoodLaExtractor() { override var mainUrl = "https://dood.ws" } +class DoodYtExtractor : DoodLaExtractor() { + override var mainUrl = "https://dood.yt" +} open class DoodLaExtractor : ExtractorApi() { override var name = "DoodStream" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt index eddbf6df..3e38b446 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Evolaod.kt @@ -16,26 +16,7 @@ open class Evoload : ExtractorApi() { override suspend fun getUrl(url: String, referer: String?): List { - val lang = url.substring(0, 2) - val flag = - if (lang == "vo") { - " \uD83C\uDDEC\uD83C\uDDE7" - } - else if (lang == "vf"){ - " \uD83C\uDDE8\uD83C\uDDF5" - } else { - "" - } - - val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// - url - } else { - url.substring(2, url.length) - } - //println(lang) - //println(cleaned_url) - - val id = cleaned_url.replace("https://evoload.io/e/", "") // wanted media id + val id = url.replace("https://evoload.io/e/", "") // wanted media id val csrv_token = app.get("https://csrv.evosrv.com/captcha?m412548=").text // whatever that is val captchaPass = app.get("https://cd2.evosrv.com/html/jsx/e.jsx").text.take(300).split("captcha_pass = '")[1].split("\'")[0] //extract the captcha pass from the js response (located in the 300 first chars) val payload = mapOf("code" to id, "csrv_token" to csrv_token, "pass" to captchaPass) @@ -44,9 +25,9 @@ open class Evoload : ExtractorApi() { return listOf( ExtractorLink( name, - name + flag, + name, link, - cleaned_url, + url, Qualities.Unknown.value, ) ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index bc910a7e..382ca756 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -13,6 +13,11 @@ class FileMoon : Filesim() { override val name = "FileMoon" } +class FileMoonSx : Filesim() { + override val mainUrl = "https://filemoon.sx" + override val name = "FileMoonSx" +} + open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index b77617c2..b7477242 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -77,6 +77,10 @@ class StreamSB10 : StreamSB() { override var mainUrl = "https://sbplay2.xyz" } +class StreamSB11 : StreamSB() { + override var mainUrl = "https://sbbrisk.com" +} + // This is a modified version of https://github.com/jmir1/aniyomi-extensions/blob/master/src/en/genoanime/src/eu/kanade/tachiyomi/animeextension/en/genoanime/extractors/StreamSBExtractor.kt // The following code is under the Apache License 2.0 https://github.com/jmir1/aniyomi-extensions/blob/master/LICENSE open class StreamSB : ExtractorApi() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt index 5109acc3..86bd9e0b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Uqload.kt @@ -7,6 +7,10 @@ class Uqload1 : Uqload() { override var mainUrl = "https://uqload.com" } +class Uqload2 : Uqload() { + override var mainUrl = "https://uqload.co" +} + open class Uqload : ExtractorApi() { override val name: String = "Uqload" override val mainUrl: String = "https://www.uqload.com" @@ -15,30 +19,14 @@ open class Uqload : ExtractorApi() { override suspend fun getUrl(url: String, referer: String?): List? { - val lang = url.substring(0, 2) - val flag = - if (lang == "vo") { - " \uD83C\uDDEC\uD83C\uDDE7" - } - else if (lang == "vf"){ - " \uD83C\uDDE8\uD83C\uDDF5" - } else { - "" - } - - val cleaned_url = if (lang == "ht") { // if url doesn't contain a flag and the url starts with http:// - url - } else { - url.substring(2, url.length) - } - with(app.get(cleaned_url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" + with(app.get(url)) { // raised error ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED (3003) is due to the response: "error_nofile" srcRegex.find(this.text)?.groupValues?.get(1)?.replace("\"", "")?.let { link -> return listOf( ExtractorLink( name, - name + flag, + name, link, - cleaned_url, + url, Qualities.Unknown.value, ) ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt new file mode 100644 index 00000000..67e59281 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vido.kt @@ -0,0 +1,34 @@ +package com.lagradost.cloudstream3.extractors +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.getAndUnpack + +class Vido : ExtractorApi() { + override var name = "Vido" + override var mainUrl = "https://vido.lol" + private val srcRegex = Regex("""sources:\s*\["(.*?)"\]""") + override val requiresReferer = true + + override suspend fun getUrl(url: String, referer: String?): List? { + val methode = app.get(url.replace("/e/", "/embed-")) // fix wiflix and mesfilms + with(methode) { + if (!methode.isSuccessful) return null + //val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull() + srcRegex.find(this.text)?.groupValues?.get(1)?.let { link -> + return listOf( + ExtractorLink( + name, + name, + link, + url, + Qualities.Unknown.value, + true, + ) + ) + } + } + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index b0dba9ff..6540b8c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -229,6 +229,7 @@ val extractorApis: MutableList = arrayListOf( StreamSB8(), StreamSB9(), StreamSB10(), + StreamSB11(), SBfull(), // Streamhub(), cause Streamhub2() works Streamhub2(), @@ -254,6 +255,7 @@ val extractorApis: MutableList = arrayListOf( // WatchSB(), 'cause StreamSB.kt works Uqload(), Uqload1(), + Uqload2(), Evoload(), Evoload1(), VoeExtractor(), @@ -277,6 +279,7 @@ val extractorApis: MutableList = arrayListOf( DoodShExtractor(), DoodWatchExtractor(), DoodWfExtractor(), + DoodYtExtractor(), AsianLoad(), @@ -324,6 +327,8 @@ val extractorApis: MutableList = arrayListOf( Filesim(), FileMoon(), + FileMoonSx(), + Vido(), Linkbox(), Acefile(), SpeedoStream(), From e5834d485b1447a3687891d698f776e7922289d8 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 25 Feb 2023 20:37:54 +0100 Subject: [PATCH 246/273] Translated using Weblate (German) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Polish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Portuguese) Currently translated at 81.0% (493 of 608 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 99.5% (605 of 608 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Italian) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (608 of 608 strings) Translated using Weblate (Polish) Currently translated at 97.3% (592 of 608 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Japanese) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (qt (generated) (qt)) Currently translated at 50.4% (304 of 602 strings) Translated using Weblate (Slovak) Currently translated at 31.7% (191 of 602 strings) Translated using Weblate (Portuguese) Currently translated at 76.9% (463 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Persian) Currently translated at 20.0% (121 of 602 strings) Translated using Weblate (Hungarian) Currently translated at 55.6% (335 of 602 strings) Translated using Weblate (German) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Russian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Urdu) Currently translated at 72.2% (435 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Hebrew) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 94.1% (567 of 602 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (583 of 602 strings) Translated using Weblate (Turkish) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Polish) Currently translated at 98.0% (590 of 602 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 88.3% (532 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Italian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (French) Currently translated at 97.3% (586 of 602 strings) Translated using Weblate (Greek) Currently translated at 97.0% (584 of 602 strings) Translated using Weblate (Czech) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (602 of 602 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Japanese) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (qt (generated) (qt)) Currently translated at 50.4% (304 of 602 strings) Translated using Weblate (Slovak) Currently translated at 31.7% (191 of 602 strings) Translated using Weblate (Portuguese) Currently translated at 76.9% (463 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Somali) Currently translated at 94.3% (568 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 44.5% (268 of 602 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Esperanto) Currently translated at 27.5% (166 of 602 strings) Translated using Weblate (Persian) Currently translated at 20.0% (121 of 602 strings) Translated using Weblate (Hungarian) Currently translated at 55.6% (335 of 602 strings) Translated using Weblate (German) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Russian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Kannada) Currently translated at 35.2% (212 of 602 strings) Translated using Weblate (Urdu) Currently translated at 72.2% (435 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Tamil) Currently translated at 18.2% (110 of 602 strings) Translated using Weblate (Hebrew) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Bengali) Currently translated at 38.7% (233 of 602 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 94.1% (567 of 602 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (583 of 602 strings) Translated using Weblate (Turkish) Currently translated at 97.1% (585 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Tagalog) Currently translated at 56.1% (338 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Swedish) Currently translated at 74.9% (451 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Romanian) Currently translated at 73.0% (440 of 602 strings) Translated using Weblate (Polish) Currently translated at 98.0% (590 of 602 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 88.3% (532 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Dutch) Currently translated at 75.0% (452 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Malayalam) Currently translated at 37.2% (224 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Macedonian) Currently translated at 48.6% (293 of 602 strings) Translated using Weblate (Italian) Currently translated at 99.1% (597 of 602 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (Hindi) Currently translated at 37.7% (227 of 602 strings) Translated using Weblate (French) Currently translated at 97.3% (586 of 602 strings) Translated using Weblate (Greek) Currently translated at 97.0% (584 of 602 strings) Translated using Weblate (Czech) Currently translated at 100.0% (602 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 77.4% (466 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Bulgarian) Currently translated at 94.5% (569 of 602 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (602 of 602 strings) Co-authored-by: Aitor Salaberria Co-authored-by: Allan Nordhøy Co-authored-by: Anarchydr Co-authored-by: Anonymous Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Fjuro Co-authored-by: Geovani Amaral Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: MedRAM Co-authored-by: Prathap Rathod Co-authored-by: Rex_sa Co-authored-by: Sandyran Co-authored-by: Sdarfeesh Co-authored-by: Translator-3000 Co-authored-by: Walter H Co-authored-by: gallegonovato Co-authored-by: gnu-ewm Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bp/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/eo/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ja/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ur/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 8 ++++++- app/src/main/res/values-cs/strings.xml | 8 ++++++- app/src/main/res/values-de/strings.xml | 13 ++++++++++- app/src/main/res/values-es/strings.xml | 8 ++++++- app/src/main/res/values-hr/strings.xml | 5 +++++ app/src/main/res/values-in/strings.xml | 5 +++++ app/src/main/res/values-it/strings.xml | 10 +++++++++ app/src/main/res/values-kn/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 17 ++++++++++++++ app/src/main/res/values-pt/strings.xml | 31 ++++++++++++++++++++++++-- app/src/main/res/values-qt/strings.xml | 23 ++++++++++--------- app/src/main/res/values-uk/strings.xml | 9 +++++++- app/src/main/res/values-zh/strings.xml | 5 +++++ 13 files changed, 125 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 1e9bcfcc..cfd761e3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -259,7 +259,7 @@ لا تظهر مرة أخرى تخطي هذا التحديث تحديث - جودة المشاهدة المفضلة + جودة المشاهدة المفضلة (WiFi) أقصى عدد لحروف عنوان مُشغل الفيديو أبعاد مُشغل الفيديو حجم ذاكرة التخزين المؤقت للفيديو @@ -555,4 +555,10 @@ تم إصدار الحلقة %d! مشترك مشترك في %s + تجاوز مزود خدمة الإنترنت + استرجاع + فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. + تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + وكيل raw.githubusercontent.com + جودة المشاهدة المفضلة (بيانات الجوال) \ 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 966cd7d9..e99e1010 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -245,7 +245,7 @@ Již nezobrazovat Přeskočit tuto aktualizace Aktualizovat - Upřednostněná kvalita sledování + Upřednostněná kvalita sledování (WiFi) Maximální počet znaků v názvu přehrávače Rozlišení přehrávače Velikost vyrovnávací paměti videa @@ -547,4 +547,10 @@ Odhlášen odběr od %s Byla vydána epizoda %d! Odebíráno + Proxy raw.githubusercontent.com + Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. + Upřednostněná kvalita sledování (mobilní data) + Vrátit zpět + Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Obcházení ISP \ 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 f6583c20..c5e74a60 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -263,7 +263,7 @@ Nicht mehr anzeigen Update ignorieren Update - Bevorzugte Auflösung + Bevorzugte Videoqualität (WLAN) Videoplayertitel max. Zeichen Videoplayer Auflösung Videopuffergröße @@ -518,4 +518,15 @@ Log Start Neustarten + Bevorzugte Videoqualität (mobile Daten) + Umgehung der GitHub Sperre mit jsdelivr, kann zu einigen Tagen Verzögerung bei Updates führen. + %s abonniert + %s deabonniert + Episode %d erschienen! + raw.githubusercontent.com Proxy + GitHub kann nicht erreicht werden, der jsdelivr-Proxy wird aktiviert. + Aktualisierung abonnierter Sendungen + Rückgängig + Abonniert + ISP-Umgehungen \ 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 2040169b..18647ef8 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -24,7 +24,7 @@ Ocultar la calidad de video en los resultados de búsqueda Diseño Diseño - Calidad de visualización preferida + Calidad de visualización preferida (WiFi) Reproductor de video preferido Diseño para emulador Diseño de la aplicación @@ -523,4 +523,10 @@ Darse de baja de %s Actualizando los programas suscritos ¡Episodio %d publicado! + Proxy raw.githubusercontent.com + No se ha podido acceder a GitHub, activando el proxy jsdelivr. + Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Revertir + ISP Bypasses + Calidad de visualización preferida (Datos móviles) \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 926c7f57..b623ec5d 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -548,4 +548,9 @@ Pretplaćeno Pretplaćen na %s Otkazana pretplata sa %s + Vraćanje + ISP zaobilaznice + raw.githubusercontent.com Proxy + Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. + Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. \ 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 46d61e44..84179352 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -546,4 +546,9 @@ Berlangganan ke %s Berhenti berlangganan di %s Episode %d telah rilis! + raw.githubusercontent.com Proksi + Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. + Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Bypass ISP + Pulihkan \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 89f6b4ee..d6bdc204 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -540,4 +540,14 @@ Ferma Superato Fallito + Proxy raw.githubusercontent.com + Disiscritto da %s + Iscritto + Iscritto a %s + Impossibile contattare GitHub, abilitazione proxy jsdelivr avviata. + Bypassa il blocco di GitHub utilizzando jsdelivr, potrebbe causare un ritardo di alcuni giorni. + Baypass ISP + Ripristina + Aggiornando shows a cui sei iscritto + L\'episodio %d è stato rilasciato! \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index c36459b7..242653be 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -85,7 +85,7 @@ ಶೇರ್ ಫೈಲ್ ಅಳಿಸಿ ಹೆಚ್ಚಿನ ಮಾಹಿತಿ - ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ + ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ \n%s-%s ಲೋಡಿಂಗ್… ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 411f0b45..bbaaec57 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -514,4 +514,21 @@ Android TV Pokazany odtwarzacz — ilość przewijania Używana ilość przewijania, gdy ukryty jest odtwarzacz + Dziennik + Uruchom ponownie + Rozpocznij + Nie powiodło się + Ukończone powodzeniem + Serwer pośredniczący raw.githubusercontent.com + Obejścia ISP + Test dostawcy + Zatrzymaj + Przywróć + Aktualizowanie subskrybowanych programów + Zasubskrybowano + Zasubskrybowano %s + Anulowano subskrypcję %s + Został wydany odcinek %d! + Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. + Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. \ 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 9353664e..3754de8b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -6,12 +6,12 @@ Episódio %d será lançado em Poster Capa do Episódio - \@string/result_poster_img_des + Poster Capa Principal Próximo Aleatório Voltar Trocar Provedor - %dd %dh %dm + %d dia(s), %d hora(s) e %d mese(s) Fonte Resolução Extras @@ -381,4 +381,31 @@ Todas as legendas em maiúsculas Transferir todos os plugins deste repositório\? %s (Desativado) + Instalador APK + %d minuto(s) + Reproduzir trailer + Marcar como visto/não visto + Reproduzir + Instalar automaticamente todas as extensões dos repositórios cadastrados. + Baixar extensões automaticamente + Refazer o processo de configuração + -30 + Vídeo + +30 + %s %d%s + Elenco: %s + Atualização em andamento + Log + Alguns aparelhos não possuem suporte para o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. + %d-%d + %d %s + Iniciar + Falha + Sucesso + Biblioteca + Navegar + Aplicativo de Anime pelos mesmos desenvolvedores + Ova + Anime + Player visível - Procurar valor \ 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 b36f3b16..c1119bfc 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -172,30 +172,31 @@ oouuh haa oohahaha hahha ooooohaha oohahaha hahha ooooohaha haaoou - u ahhu uuuh hau uaohuau + u ahhu uuuh hau uaohuau aahuuouhh ouh hhhah hhaohuhha a auoo ohauh - uhaauauau ahuuouaha + uhaauauau ahuuouaha auuuha h a ahuhaaaa - uaoh uhu uahaaaaoo - uauhah u aao u oah - h u ahahh aoou ha + uaoh uhu uahaaaaoo + uauhah u aao u oah + h u ahahh aoou ha haoooo aaoou uou ah oahuouooaouoa ouuhh o ouou uhauuuoaah h ou aouhouo aaooao hh - hhauhohhuu au aaohu - uhuoh o a ohahuhohoa hah + hhauhohhuu au aaohu + uhuoh o a ohahuhohoa hah ua hu ouo o aoau hah ah - ah huu oouhhau aoaoaaohoo ha - a ahu uoo uoahuo uo + ah huu oouhhau aoaoaaohoo ha + a ahu uoo uoahuo uo uo u ohouao uuoouhh hhuhuuh ouhoaao hau aouo - uha uh huo uooaah u + uha uh huo uooaah u u ooah uo ahauao huhuu hauu h a ou oh ouhuouhoaaha aaooohhouhhha hauauuu - aaaaaaa uuuuuu\n%s -> %s + aaaaaaa uuuuuu +\n%s -> %s %s aaou %d oouaaahh %s aaaaaaugh ouh %d uuoogahaaah ooua-h-ha diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5330d3ec..a676b583 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -217,7 +217,7 @@ Пропустити OP Не показувати знову Оновити - Бажана якість перегляду + Бажана якість перегляду (WiFi) Заголовок Перемикання елементів інтерфейсу на плакаті Оновлення не знайдено @@ -523,4 +523,11 @@ Підписано на %s Відписатися від %s Епізод %d випущено! + Повернути + raw.githubusercontent.com +\nProxy + Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. + Обходи ISP + Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9e2d6137..626cc0fe 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -571,4 +571,9 @@ 第 %d 集已发布! 成功 日志 + raw.githubusercontent.com 代理 + 连接 Github 失败,正在启用 jsdelivr 代理。 + 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + ISP 绕过 + 还原 \ No newline at end of file From d6df24eff2d425fc79e52b94a1fe600f857ec19a Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:05:42 -0600 Subject: [PATCH 247/273] Fixes on filesim and added filemoon, ztreamhub (#397) * fix fastream, tomatomatela, and added okrulink * forgot this * sendvid extractor * sendvid extractor * fixes * Filesim fix, added filemoon and ztreamhub --- .../cloudstream3/extractors/Filesim.kt | 45 +++++++++---------- .../cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 382ca756..84fd0552 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -1,13 +1,15 @@ package com.lagradost.cloudstream3.extractors -import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson -import java.net.URI +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 + +class Ztreamhub : Filesim() { + override val mainUrl: String = "https://ztreamhub.com" //Here 'cause works + override val name = "Zstreamhub" +} class FileMoon : Filesim() { override val mainUrl = "https://filemoon.to" override val name = "FileMoon" @@ -29,34 +31,27 @@ open class Filesim : ExtractorApi() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - with(app.get(url).document) { - this.select("script").forEach { script -> - if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val data = getAndUnpack(script.data()) - val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach - val fixedData = foundData.replace("file:", """"file":""") - - parseJson>("[$fixedData]").forEach { - callback.invoke( - ExtractorLink( - name, - name, - it.file, - "$mainUrl/", - Qualities.Unknown.value, - URI(it.file).path.endsWith(".m3u8") - ) - ) - } + val response = app.get(url, referer = mainUrl).document + response.select("script[type=text/javascript]").map { script -> + if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) { + val unpackedscript = getAndUnpack(script.data()) + val m3u8Regex = Regex("file.\\\"(.*?m3u8.*?)\\\"") + val m3u8 = m3u8Regex.find(unpackedscript)?.destructured?.component1() ?: "" + if (m3u8.isNotEmpty()) { + generateM3u8( + name, + m3u8, + mainUrl + ).forEach(callback) } } } } - private data class ResponseSource( + /* private data class ResponseSource( @JsonProperty("file") val file: String, @JsonProperty("type") val type: String?, @JsonProperty("label") val label: String? - ) + ) */ } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 6540b8c4..0bced6b2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -370,6 +370,7 @@ val extractorApis: MutableList = arrayListOf( Cda(), Dailymotion(), ByteShare(), + Ztreamhub() ) From ab324b93e89b2e955436d7ec2099b3256cb0d005 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Tue, 28 Feb 2023 01:19:59 +0100 Subject: [PATCH 248/273] Small fixes to Intents and Subscriptions --- .../lagradost/cloudstream3/MainActivity.kt | 6 ++++- .../services/SubscriptionWorkManager.kt | 22 ++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index e626dcd6..a7449255 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -319,7 +319,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } else if (safeURI(str)?.scheme == appStringSearch) { nextSearchQuery = URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8") - nav_view.selectedItemId = R.id.navigation_search + + // Use both navigation views to support both layouts. + // It might be better to use the QuickSearch. + nav_view?.selectedItemId = R.id.navigation_search + nav_rail_view?.selectedItemId = R.id.navigation_search } else if (safeURI(str)?.scheme == appStringResumeWatching) { val id = str.substringAfter("$appStringResumeWatching://").toIntOrNull() 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 d1b1b660..adf5abfa 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/services/SubscriptionWorkManager.kt @@ -105,14 +105,12 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete SUBSCRIPTION_CHANNEL_DESCRIPTION ) - safeApiCall { - setForeground( - ForegroundInfo( - SUBSCRIPTION_NOTIFICATION_ID, - progressNotificationBuilder.build() - ) + setForeground( + ForegroundInfo( + SUBSCRIPTION_NOTIFICATION_ID, + progressNotificationBuilder.build() ) - } + ) val subscriptions = getAllSubscriptions() @@ -196,7 +194,15 @@ class SubscriptionWorkManager(val context: Context, workerParams: WorkerParamete PendingIntent.getActivity(context, 0, intent, 0) } - val poster = ioWork { savedData.posterUrl?.let { url -> context.getImageBitmapFromUrl(url, savedData.posterHeaders) } } + val poster = ioWork { + savedData.posterUrl?.let { url -> + context.getImageBitmapFromUrl( + url, + savedData.posterHeaders + ) + } + } + val updateNotification = updateNotificationBuilder.setContentTitle(updateHeader) .setContentText(updateDescription) From f0515c4dc9e38bcf80f9766e7dff9f9ffd802f72 Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Fri, 3 Mar 2023 09:24:02 +0000 Subject: [PATCH 249/273] Support qualities for Dailymotion (#407) * Dailymotion qualities --- .../cloudstream3/extractors/Dailymotion.kt | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt index 125e4bcf..4b7cb19f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt @@ -6,6 +6,7 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 import com.lagradost.cloudstream3.utils.Qualities import java.net.URL @@ -42,18 +43,9 @@ open class Dailymotion : ExtractorApi() { ) val metaData = app.get(metaDataUrl, referer = embedUrl, cookies = cookies) .parsedSafe() ?: return - metaData.qualities.forEach { (key, video) -> + metaData.qualities.forEach { (_, video) -> video.forEach { - callback.invoke( - ExtractorLink( - name, - "$name $key", - it.url, - "", - Qualities.Unknown.value, - true - ) - ) + getStream(it.url, this.name, callback) } } } @@ -75,6 +67,17 @@ open class Dailymotion : ExtractorApi() { return null } + private suspend fun getStream( + streamLink: String, + name: String, + callback: (ExtractorLink) -> Unit + ) { + return generateM3u8( + name, + streamLink, + "", + ).forEach(callback) + } data class Config( val context: Context, val dmInternalData: InternalData From 76545f55c3efeb66e14c6a4c62d2c980adf51830 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Fri, 3 Mar 2023 17:45:26 +0100 Subject: [PATCH 250/273] Standardized some home screen padding and made subtitle delay persistent. Fixes #405 --- .../ui/player/FullScreenPlayer.kt | 10 +++++++ .../cloudstream3/ui/player/GeneratorPlayer.kt | 29 +++++++++---------- .../main/res/layout/fragment_home_head.xml | 28 +++++++----------- app/src/main/res/layout/homepage_parent.xml | 2 -- 4 files changed, 34 insertions(+), 35 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 d1b2814d..86e21fd6 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 @@ -84,6 +84,7 @@ const val HORIZONTAL_MULTIPLIER = 2.0f const val DOUBLE_TAB_MAXIMUM_HOLD_TIME = 200L const val DOUBLE_TAB_MINIMUM_TIME_BETWEEN = 200L // this also affects the UI show response time const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15 // in both directions +private const val SUBTITLE_DELAY_BUNDLE_KEY = "subtitle_delay" // All the UI Logic for the player open class FullScreenPlayer : AbstractPlayerFragment() { @@ -1120,11 +1121,20 @@ open class FullScreenPlayer : AbstractPlayerFragment() { resetRewindText() } + override fun onSaveInstanceState(outState: Bundle) { + // As this is video specific it is better to not do any setKey/getKey + outState.putLong(SUBTITLE_DELAY_BUNDLE_KEY, subtitleDelay) + super.onSaveInstanceState(outState) + } + @SuppressLint("ClickableViewAccessibility") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // init variables setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) + savedInstanceState?.getLong(SUBTITLE_DELAY_BUNDLE_KEY)?.let { + subtitleDelay = it + } // handle tv controls playerEventListener = { eventType -> 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 67f58195..46f2bca9 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 @@ -13,7 +13,6 @@ import android.view.View import android.view.ViewGroup import android.widget.* import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog import androidx.core.animation.addListener import androidx.core.content.ContextCompat import androidx.core.view.isGone @@ -734,19 +733,17 @@ class GeneratorPlayer : FullScreenPlayer() { } val currentAudioTracks = tracks.allAudioTracks - val trackBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) - .setView(R.layout.player_select_tracks) - - val tracksDialog = trackBuilder.create() + val trackDialog = Dialog(ctx, R.style.AlertDialogCustomBlack) + trackDialog.setContentView(R.layout.player_select_tracks) + trackDialog.show() // selectTracksDialog = tracksDialog - tracksDialog.show() - val videosList = tracksDialog.video_tracks_list - val audioList = tracksDialog.auto_tracks_list + val videosList = trackDialog.video_tracks_list + val audioList = trackDialog.auto_tracks_list - tracksDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1 - tracksDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1 + trackDialog.video_tracks_holder.isVisible = currentVideoTracks.size > 1 + trackDialog.audio_tracks_holder.isVisible = currentAudioTracks.size > 1 fun dismiss() { if (isPlaying) { @@ -781,7 +778,7 @@ class GeneratorPlayer : FullScreenPlayer() { videosList.setItemChecked(which, true) } - tracksDialog.setOnDismissListener { + trackDialog.setOnDismissListener { dismiss() // selectTracksDialog = null } @@ -811,11 +808,11 @@ class GeneratorPlayer : FullScreenPlayer() { audioList.setItemChecked(which, true) } - tracksDialog.cancel_btt?.setOnClickListener { - tracksDialog.dismissSafe(activity) + trackDialog.cancel_btt?.setOnClickListener { + trackDialog.dismissSafe(activity) } - tracksDialog.apply_btt?.setOnClickListener { + trackDialog.apply_btt?.setOnClickListener { val currentTrack = currentAudioTracks.getOrNull(audioIndexStart) player.setPreferredAudioTrack( currentTrack?.language, currentTrack?.id @@ -828,7 +825,7 @@ class GeneratorPlayer : FullScreenPlayer() { player.setMaxVideoSize(width, height, currentVideo?.id) } - tracksDialog.dismissSafe(activity) + trackDialog.dismissSafe(activity) } } } catch (e: Exception) { @@ -1145,7 +1142,7 @@ class GeneratorPlayer : FullScreenPlayer() { val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL" - val title = when (titleRez) { + val title = when (titleRez) { 0 -> "" 1 -> extra 2 -> source diff --git a/app/src/main/res/layout/fragment_home_head.xml b/app/src/main/res/layout/fragment_home_head.xml index 0ee50042..603621f7 100644 --- a/app/src/main/res/layout/fragment_home_head.xml +++ b/app/src/main/res/layout/fragment_home_head.xml @@ -1,7 +1,6 @@ - @@ -148,17 +145,16 @@ + app:drawableTint="?attr/white" /> @@ -184,9 +180,9 @@ + android:layout_height="wrap_content" + android:background="?android:attr/selectableItemBackground"> + app:drawableTint="?attr/white" /> diff --git a/app/src/main/res/layout/homepage_parent.xml b/app/src/main/res/layout/homepage_parent.xml index b2f3e0a7..9c5dc84d 100644 --- a/app/src/main/res/layout/homepage_parent.xml +++ b/app/src/main/res/layout/homepage_parent.xml @@ -23,9 +23,7 @@ android:nextFocusUp="@id/home_child_more_info" android:paddingHorizontal="5dp" android:clipToPadding="false" - android:descendantFocusability="afterDescendants" - app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" android:id="@+id/home_child_recyclerview" android:orientation="horizontal" From 1eaa4620dc7ec00d34486d1e6ce6a7a1c38afe77 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 2 Mar 2023 05:38:39 +0100 Subject: [PATCH 251/273] Translated using Weblate (qt (generated) (qt)) Currently translated at 54.5% (333 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (French) Currently translated at 98.8% (603 of 610 strings) Translated using Weblate (Italian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (German) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 99.6% (608 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 85.0% (519 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Vietnamese) Currently translated at 96.8% (591 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Polish) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Anarchydr Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Duc Nguyen Tien Co-authored-by: Felipe Nogueira Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: Massimo Pissarello Co-authored-by: Samuel Gadiel Co-authored-by: Sdarfeesh Co-authored-by: Walter H Co-authored-by: eightyy8 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/qt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-es/strings.xml | 8 +- app/src/main/res/values-fr/strings.xml | 17 +- app/src/main/res/values-hr/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 3 +- app/src/main/res/values-it/strings.xml | 3 +- app/src/main/res/values-pl/strings.xml | 3 +- app/src/main/res/values-pt/strings.xml | 217 +++++++++++++++++++------ app/src/main/res/values-qt/strings.xml | 29 ++++ app/src/main/res/values-ru/strings.xml | 13 +- app/src/main/res/values-vi/strings.xml | 23 ++- app/src/main/res/values-zh/strings.xml | 3 +- 12 files changed, 254 insertions(+), 68 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index c5e74a60..7cf49de1 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -246,7 +246,7 @@ In Browser wiedergeben Link kopieren Auto-Download - Download-Mirror + Alternativer Download Links neu laden Untertitel herunterladen Qualitätsanzeige diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 18647ef8..0b195275 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -506,11 +506,11 @@ \nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local ¡Se encontró un archivo en modo seguro! \nNo cargar ninguna extensión al inicio hasta que se elimine el archivo. - Jugadora mostrada - buscar cantidad - Jugadora oculta - buscar cantidad + Reproductor visible - buscar cantidad + Reproductor oculto - buscar cantidad Android TV - La cantidad de búsqueda utilizada cuando la jugadora es visible - La cantidad de búsqueda utilizada cuando el jugador está oculto + Tiempo de búsqueda usado (en segundos) cuando el reproductor está visible + Tiempo de búsqueda usado (en segundos) cuando el reproductor está oculto Parar Falló Registro diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 18255b3b..f3d35c19 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -130,7 +130,7 @@ Nouvelle mise à jour trouvée ! \n%s -> %s Épisode spécial - Qualité de visionnage préférée + Qualité de visionnage préférée (WiFi) Taille de la mémoire cache Étendre Non-responsabilité @@ -508,4 +508,19 @@ Mis à jour (ancien vers nouveau) Fichier du mode sans échec trouvé ! \nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé. + Arrêter + Revenir à + Enregistrer + Qualité de visionnage préférée (données mobiles) + Abonné à %s + Démarrer + Test des fournisseurs + Réussi + Désabonné de %s + Redémarrer + Abonné + raw.githubusercontent.com Proxy + Contournements de FAI + L\'épisode %d est sorti ! + Échouer \ No newline at end of file diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index b623ec5d..159542cc 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -553,4 +553,5 @@ raw.githubusercontent.com Proxy Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. + Preferirana kvaliteta gledanja (podatkovna mobilna mreža) \ 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 84179352..0e383562 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -243,7 +243,7 @@ Jangan tunjukkan lagi Skip Update ini Update - Kualitas tontonan yang lebih diinginkan + Kualitas tontonan yang lebih diinginkan (WIFI) Karakter maksimal judul pemutar video Resolusi pemutar video Ukuran buffer video @@ -551,4 +551,5 @@ Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. Bypass ISP Pulihkan + Nonton dengan kualitas yang di inginkan (Data Seluler) \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d6bdc204..b8e7eb20 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -265,7 +265,7 @@ Non mostrare di nuovo Salta questo aggiornamento Aggiorna - Risoluzione preferita + Qualità di visualizzazione preferita (WiFi) Limita i caratteri del titolo nel player Risoluzione video player Dimensione cache video @@ -550,4 +550,5 @@ Ripristina Aggiornando shows a cui sei iscritto L\'episodio %d è stato rilasciato! + Qualità di visualizzazione preferita (Dati mobili) \ 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 bbaaec57..558a46ed 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -254,7 +254,7 @@ Nie pokazuj ponownie Pomiń tę aktualizację Aktualizacja - Domyślna jakość + Domyślna jakość (WiFi) Maksymalna ilość znaków w tytule odtwarzacza Rozdzielczość odtwarzacza wideo Rozmiar bufora wideo @@ -531,4 +531,5 @@ Został wydany odcinek %d! Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. + Domyślna jakość (dane mobilne) \ 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 3754de8b..0c846361 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -3,63 +3,63 @@ %s Ep %d %dh %dm %dm - Episódio %d será lançado em + O episódio %d será lançado em Poster - Capa do Episódio + Pôster do episódio Poster - Capa Principal + Pôster Principal Próximo Aleatório Voltar - Trocar Provedor - %d dia(s), %d hora(s) e %d mese(s) + Alterar Provedor + %dd %dh %dm Fonte Resolução Extras - Preview Background + Visualizar plano de fundo Velocidade (%.2fx) Classificado: %.1f Nova atualização encontrada! \n%s -> %s - Enchimento + Preenchimento CloudStream - Reproduzir com CloudStream + Assistir com o CloudStream Início - Pesquisa - Transferências - Opções + Pesquisar + Downloads + Configurações Procurar… - Procurar em %s… - Sem Dados - Mais Opções + Pesquisar %s… + Sem dados + Mais opções Próximo episódio - Géneros - Partilhar - Abrir no Navegador - Saltar Carga + Gêneros + Compartilhar + Abrir no navegador + Pular carregamento Carregando… Assistindo - Em Espera + Em espera Concluído - Abandonado - Planeio Assistir - Nenhuma - Assistindo de Novo - Reproduzir Filme - Reproduzir Livestream + Desistido + Pretendo assistir + Nenhum + Reassistindo + Reproduzir filme + Reproduzir transmissão ao vivo Transmitir Torrent Fontes Legendas - Voltar a tentar ligação… - Voltar Atrás - Reproduzir Episódio - Transferir - Transferido - A Transferir - Transferência em Pausa - Transferência Iniciada - Transferência Falhou - Transferência Cancelada - Transferência Completa + Tentar conexão novamente… + Voltar + Reproduzir episódio + Download + Baixado + Baixando + Download Pausado + Download Iniciado + Falha no Download + Download cancelado + Download concluído Stream Erro a Carregar Links Armazenamento Interno @@ -142,7 +142,7 @@ Arquivo de backup carregado Falha ao restaurar dados do ficheiro %s Dados guardados com sucesso - Permissões de armazenamento em falta, por favor tente de novo + Permissão de armazenamento não encontrada, por favor tente novamente. Erro no backup de %s Procurar Contas @@ -250,15 +250,15 @@ Não mostrar de novo Saltar esta Atualização Atualizar - Qualidade Preferida - Máximo de caracteres do título de vídeos + Qualidade Preferida (WiFi) + Máximo de caracteres do título no player de video Resolução do player de vídeo Tamanho do buffer do vídeo Comprimento do buffer do vídeo Cache do vídeo em disco Limpar cache de vídeo e imagem - Causará travamentos aleatórios se definido muito alto. Não mude se tiver pouca memória RAM, como um Android TV ou um telefone antigo - Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV + Causará travamentos em dispositivos com pouca memória se definido muito alto , como uma Android TV. + Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como uma Android TV. DNS sobre HTTPS Útil para contornar bloqueios do fornecedor de internet Clonar site @@ -363,7 +363,7 @@ Plugin Carregado Plugin Apagado Falha ao carregar %s - Iniciada a transferência %d %s + Download iniciado %d %s… Transferido %d %s com sucesso Tudo %s já transferido Transferência em batch @@ -375,18 +375,22 @@ Transferido: %d Desativado: %d Não transferido: %d - Adicionar um repositório para instalar extensões de sites + O CloudStream não possui sites instalados por padrão. Você precisa instalar os sites a partir de repositórios. +\n +\nDevido a uma restrição sem sentido de direitos autorais (DMCA) pela Sky UK Limited 🤮 não podemos vincular o site do repositório no aplicativo. +\n +\nJunte-se ao nosso Discord ou pesquise online. Ver repositórios da comunidade Lista pública Todas as legendas em maiúsculas Transferir todos os plugins deste repositório\? %s (Desativado) Instalador APK - %d minuto(s) - Reproduzir trailer + %d min + Assistir Trailer Marcar como visto/não visto Reproduzir - Instalar automaticamente todas as extensões dos repositórios cadastrados. + Instalar automaticamente todos os plugins ainda não instalados dos repositórios adicionados. Baixar extensões automaticamente Refazer o processo de configuração -30 @@ -394,9 +398,9 @@ +30 %s %d%s Elenco: %s - Atualização em andamento + Atualização iniciada Log - Alguns aparelhos não possuem suporte para o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. + Alguns aparelhos não suportam o novo instalador de pacotes. Use a opção legado caso não esteja conseguindo atualizar. %d-%d %d %s Iniciar @@ -408,4 +412,121 @@ Ova Anime Player visível - Procurar valor + Instalando atualização do app… + Você tem certeza que deseja sair\? + Versão + Encerramento + Limpar histórico + Abertura + Não + Ordenar por + Sim + Baixando atualização do app… + Episódio %d lançado! + Créditos + Descrição + Tamanho + Parar + Modo seguro ligado + Histórico + Ordenar + Player interno + Autores + Suportado + Idioma + Instalar a extensão primeiro + Playlist HLS + Player de vídeo preferido + Estado + Gestos + Faixas + WP + Cam + Abertura + Selecionar Biblioteca + Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns 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 + Final misto + Avaliações (Decrescente) + Aplicar ao reiniciar + Referente + Player oculto - Quantidade de Busca + raw.githubusercontent.com Proxy + Blu-ray + Aparência + 1000 ms + SDR + 18+ + Abrir com + Teste de provedor + UHD + Ver informações sobre falha + Aplicativo não encontrado + Reverter + Link para transmitir + Plugins baixados + %d plugins atualizados + Pular %s + Abertura mista + Alfabético (Z a A) + Parece que esta lista está vazia, tente trocar para outra + Inscrito em %s + 4K + Faixas de vídeo + O aplicativo será atualizado ao sair + Atualizando shows inscritos + Alfabético (A a Z) + Avaliações (Crescente) + Parece que a sua biblioteca está vazia :( +\nFaça login em uma conta de biblioteca ou adicione shows à sua biblioteca local + Arquivo de modo de segurança encontrado! +\nNenhuma extensão será carregada na inicialização do app até que o arquivo seja removido. + Contorno do provedor de serviço de internet (ISP) + Links + Recursos do Player + Recursos + Atualizações de aplicativos + Qualidade Preferida (Dados Móveis) + Quantidade de busca (em segundos) usada quando o player de video está visível + Quantidade de busca (em segundos) usada quando o player de video está oculto + Falha ao conectar com GitHub, ativando proxy jsdelivr. + Cache + Android TV + Legendas + %s %s + TS + Cam + Cam + HQ + HD + TC + Web + Nota: %s + Legado + Todas as extensões foram desativadas devido a uma falha para ajudá-lo a encontrar a que está causando o problema. + Recapitular + Mostrar pop-ups para pular abertura/encerramento + Muito texto. Não é possível salvar na área de transferência. + Marcar como assistido + Backup + Extensões + Ações + Layout + Configurações padrão + SD + Faixas de áudio \ 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 c1119bfc..76852ca4 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -219,4 +219,33 @@ uuuuhhhoouuooog ooaaahhhh uuu ugggg ooo guggg ooh + auuuooohaaaaagh + uuuuuuuh aaaoo o + ooooooouuuua aa aaagh agh + AAAAUUUOH + aoughoooaaaa + oooouuuh + ahaough aaouuuuh-h + auughooo + ooooooa aauoh + aaaaagh oouoo aaaaaaa + aaaaaagh uuohuoh + aaaaaauo agghhhhhhaoouu + uuuuuuuuh + ouaaahh + ooough aaoough aooou %s aaaa + ouooooouuuu oooooo + aaaaaaaaaaahhhgh-aooohoooo + aau aooooghaao + aagh aaaaaaaaaaaa oooh, aaough, ooga oguuu aaaaaaaaaaa ooooooohghh a-a-aaauo + %dmmmmmm.. +\naaaaooughugh + aooohuohaaaa ooooagh + oooooogh-aaaaaogh + guuuaaaahhhhhhhaaa + woooaaahh ahahaaaauu 🦍 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOGGAGHAGHAAA + aoaaaaaoooghhh + oooooh uuaagh + \@string/home_play \ 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 2812667a..e613cee4 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -318,7 +318,7 @@ Титры Отметить как просмотренное Разрешение видеоплеера - Желаемое качество видео + Предпочтительное качество видео (WiFi) Максимум символов Длинна буфера Кеш видео на диске @@ -518,4 +518,15 @@ Неудачный Прекратить Перезапустить + Вернуться + Подписался на %s + Предпочтительное качество видео (Мобильный интернет) + raw.githubusercontent.com Прокси-сервер + Не удалось подключиться к GitHub. Будет выполнен прокси jsdelivr. + Эпизод %d выпущен! + Обходы провайдера + Обновление подписки на фильмы и сериалы + Обход ограничения доступа к GitHub с помощью jsdelivr может задержать обновления на несколько дней. + Подписные + Отказались от подписки на %s \ 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 db647b5d..59c65916 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -10,7 +10,7 @@ %dm Poster - \@string/result_poster_img_des + Ảnh bìa Episode Poster Main Poster Next Random @@ -260,18 +260,18 @@ Kiểm tra cập nhật Khóa Thu Phóng - Tuỳ chọn - Tua nhanh + Nguồn + Bỏ qua OP Không hiện lại - Bỏ qua + Bỏ qua bản cập nhật này Cập nhật - Tự động chọn chất lượng phim + Chất lượng xem ưu tiên (WiFi) Kí tự tối đa trên tiêu đề - Định dạng trình phát - Dung lượng video cache + Độ phân giải trình phát video + Kích thước bộ nhớ đệm video Thời lượng bộ nhớ đệm - Dung lượng video cache - Xoá hình ảnh và video + Lưu bộ nhớ đệm video trên ổ cứng + Xoá bộ nhớ đệm hình ảnh và video Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng ram thấp như Android TV. Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. DNS over HTTPS @@ -519,4 +519,9 @@ Có vẻ như danh sách này trống, hãy thử chuyển sang danh sách khác Chữ cái (A đến Z) Chọn Thư viện + Nhật ký + Chất lượng xem ưu tiên (Dữ liệu di động) + Thất bại + Thành công + Bắt đầu \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 626cc0fe..72d62a04 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -286,7 +286,7 @@ 不再显示 跳过此更新 更新 - 首选播放画质 + 首选播放画质(WiFi) 视频播放器标题最多字符 视频播放器标题 视频缓冲大小 @@ -576,4 +576,5 @@ 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 ISP 绕过 还原 + 首选播放画质(移动数据) \ No newline at end of file From e85b31c35ddac86e03cfbd495e01a69cedfe56ff Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 7 Mar 2023 17:36:53 +0100 Subject: [PATCH 252/273] Fixing rouge pixels in settings --- app/src/main/res/values/styles.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 78c62c69..b9648162 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -40,7 +40,6 @@ ?attr/textColor ?attr/grayTextColor - ?attr/grayTextColor ?attr/grayTextColor ?attr/textColor From 37244ab0f74392af6861a4f894ebb1a3de77806a Mon Sep 17 00:00:00 2001 From: PokerFace <117321707+pokerface-bad@users.noreply.github.com> Date: Sat, 11 Mar 2023 02:45:11 +0700 Subject: [PATCH 253/273] Intertal Player: Added MPD support (#402) * added isDash in ExtractorLink --- .../cloudstream3/ui/player/CS3IPlayer.kt | 10 +++++----- .../cloudstream3/utils/ExtractorApi.kt | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 6 deletions(-) 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 782e3fa4..cb8efe92 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 @@ -1196,10 +1196,10 @@ class CS3IPlayer : IPlayer { HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory) } - val mime = if (link.isM3u8) { - MimeTypes.APPLICATION_M3U8 - } else { - MimeTypes.VIDEO_MP4 + val mime = when { + link.isM3u8 -> MimeTypes.APPLICATION_M3U8 + link.isDash -> MimeTypes.APPLICATION_MPD + else -> MimeTypes.VIDEO_MP4 } val mediaItems = if (link is ExtractorLinkPlayList) { @@ -1249,4 +1249,4 @@ class CS3IPlayer : IPlayer { loadOfflinePlayer(context, it) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 0bced6b2..b03c9fb7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -52,7 +52,7 @@ data class ExtractorLinkPlayList( ) -open class ExtractorLink( +open class ExtractorLink constructor( open val source: String, open val name: String, override val url: String, @@ -62,7 +62,24 @@ open class ExtractorLink( override val headers: Map = mapOf(), /** Used for getExtractorVerifierJob() */ open val extractorData: String? = null, + open val isDash: Boolean = false, ) : VideoDownloadManager.IDownloadableMinimum { + /** + * Old constructor without isDash, allows for backwards compatibility with extensions. + * Should be removed after all extensions have updated their cloudstream.jar + **/ + constructor( + source: String, + name: String, + url: String, + referer: String, + quality: Int, + isM3u8: Boolean = false, + headers: Map = mapOf(), + /** Used for getExtractorVerifierJob() */ + extractorData: String? = null + ) : this(source, name, url, referer, quality, isM3u8, headers, extractorData, false) + override fun toString(): String { return "ExtractorLink(name=$name, url=$url, referer=$referer, isM3u8=$isM3u8)" } From 8b2881f5f64fad7eb29c93af0a3f696798b93d39 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 10 Mar 2023 20:45:19 +0100 Subject: [PATCH 254/273] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 18 +-- app/src/main/res/values-cs/strings.xml | 18 +-- app/src/main/res/values-es/strings.xml | 18 +-- app/src/main/res/values-in/strings.xml | 18 +-- app/src/main/res/values-ms/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 11 +- app/src/main/res/values-pt/strings.xml | 14 +- app/src/main/res/values-ru/strings.xml | 16 +-- app/src/main/res/values-tr/strings.xml | 173 ++++++++++++++----------- app/src/main/res/values-uk/strings.xml | 101 +++++++-------- app/src/main/res/values-zh/strings.xml | 14 +- app/src/main/res/values/strings.xml | 20 +-- 12 files changed, 225 insertions(+), 198 deletions(-) create mode 100644 app/src/main/res/values-ms/strings.xml diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index cfd761e3..ae45465b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -119,16 +119,16 @@ وضع إيغنغرافي يضيف خيار السرعة في المُشغل السحب لتقديم - إسحب إلى اليسار أو اليمين للتحكم في الوقت في مُشغل الفيديو + اسحب من جانب إلى آخر للتحكم في موضعك في مقطع فيديو السحب لتغيير الإعدادات - إسحب على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت + مرر لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت تشغيل الحلقة التالية تلقائيًا تبدأ الحلقة التالية عندما تنتهي الحالية النقر مرتان للتقديم للأمام أو للخلف الضغط مرتان لإيقاف مؤقت - التحكم في مدى تقديم المُشغل + التحكم في مدى تقديم المُشغل(ثوان) إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف - إضغط في الوسط لإيقاف مؤقت + اضغط مرتين في المنتصف للتوقف استخدم سطوع النظام استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن تحديث تقدم المشاهدة @@ -155,7 +155,7 @@ تحديث الإضافات تلقائيًا تنزيل الإضافات تلقائيًا التحديث التلقائي - البحث تلقائيًا عن التحديثات الجديدة عند البداية + ابحث تلقائيا عن التحديثات الجديدة بعد بدء التطبيق. التحديث إلى الاصدارات التجريبية (بيتا) البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط غيت هاب @@ -218,8 +218,8 @@ فيلم مسلسل كرتون - أنمي - اوفا + أنيمي + أوفا تورنت وثائقي دراما آسيوية @@ -284,7 +284,7 @@ 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. عام زر العشوائي - إظهار زر العشوائي على الصفحة الرئيسية + يظهر الزر على الصفحة الرئيسية والذي يمكنه اختيار فيلم عشوائي أو مسلسل تلفزيوني من الصفحة الرئيسية لغات المزود واجهة التطبيق المحتوى المفضل @@ -558,7 +558,7 @@ تجاوز مزود خدمة الإنترنت استرجاع فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. - تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) \ 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 e99e1010..67179b46 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -122,14 +122,14 @@ Rychlostní režim Přidá do přehrávače možnost rychlosti Přejet pro posun - Přejeďte prstem vlevo nebo vpravo pro ovládání času v přehrávači + Přejeďte prstem ze strany na stranu pro ovládání své pozice ve videu Přejet pro změnu nastavení - Přejeďte prstem na levé nebo pravé straně pro změnu jasu nebo hlasitosti + Přejeďte prstem nahoru nebo dolů na levé nebo pravé straně pro změnu jasu nebo hlasitosti Dvojité klepnutí pro posun Dvojité klepnutí pro pozastavení - Množství času k posunu + Množství času k posunu (sekundy) Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad - Klepněte doprostřed pro pozastavení + Klepněte dvakrát doprostřed pro pozastavení Použít systémový jas V přehrávači použít systémov překrytí Aktualizovat postup sledování @@ -151,7 +151,7 @@ Nebude odesílat žádná data Zobrazit výplňové epizody u anime Zobrazit aktualizace aplikace - Při spuštění automaticky zkontrolovat nové aktualizace + Při spuštění aplikace automaticky zkontrolovat nové aktualizace. Aktualizovat na předběžná vydání Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání GitHub @@ -211,8 +211,8 @@ Film Seriál Animovaný - \@string/anime - \@string/ova + Anime + OVA Torrent Dokument Asijské drama @@ -266,7 +266,7 @@ Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí. Obecné Náhodné tlačítko - Zobrazit na domovské stránce náhodné tlačítko + Zobrazit na domovské stránce tlačítko, kterým lze vybrat náhodný film nebo seriál z domovské stránky Jazyk poskytovatelů Rozložení aplikace Preferovaná média @@ -551,6 +551,6 @@ Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. Upřednostněná kvalita sledování (mobilní data) Vrátit zpět - Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP \ 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 0b195275..5c8ac532 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -51,10 +51,10 @@ Elevado Use esto si los subtítulos se muestran %d ms muy pronto Use esto si los subtítulos se muestran %d ms tarde - Desliza el dedo hacia la izquierda o hacia la derecha para controlar el tiempo en el reproductor de video + Desliza el dedo de lado a lado para controlar la posición en un video Filtrar por idioma de medios preferido Eliminar Closed Captions (CC) de los subtítulos - Cantidad de tiempo de búsqueda en el reproductor (en segundos) + 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 @@ -205,16 +205,16 @@ Modo Eigengravy Deslice para avanzar/retroceder Deslice para cambiar la configuración - Deslice el dedo hacia la izquierda o hacia la derecha para cambiar el brillo o el volumen + Deslice hacia arriba o hacia abajo en el lado izquierdo o derecho para cambiar el brillo o el volumen Toca dos veces para buscar Tocar dos veces para pausar Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás - Toque en el medio para pausar + Toque dos veces en el medio para hacer una pausa Usar brillo del sistema Restaurar datos desde el backup Hacer copia de los datos (backup) Archivo de backup cargado - Buscar automáticamente nuevas actualizaciones al inicio + Busque automáticamente nuevas actualizaciones después de iniciar la aplicación. Rehacer el proceso de configuración inicial Mostrar episodio de relleno para Anime Reproducir Episodio @@ -306,7 +306,7 @@ Aspecto Características Botón de Al azar - Muestra un botón de reproducción \"al azar\" en la página de inicio + Muestra un botón de reproducción \"al azar\" en la página de inicio para poelículas y series cuenta Cerrar sesión Cambiar cuenta @@ -363,8 +363,8 @@ Película Serie Dibujo animado - \@string/anime - \@string/ova + Anime + OVA Torrent Documental Drama asiático @@ -525,7 +525,7 @@ ¡Episodio %d publicado! Proxy raw.githubusercontent.com No se ha podido acceder a GitHub, activando el proxy jsdelivr. - Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Con jsdelivr, se puede omitir el bloqueo de GitHub. Puede retrasar las actualizaciones unos días. Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 0e383562..1913868a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -120,14 +120,14 @@ Mode Eigengravy Menambahkan opsi kecepatan di pemutar Geser untuk mengubah waktu - Geser ke kiri atau kanan untuk mengontrol waktu di pemutar video + Geser dari sisi ke sisi untuk mengontrol posisi dalam video Geser untuk mengubah pengaturan - Geser ke sisi kiri atau kanan untuk mengubah pencerahan atau volume + Geser ke atas atau ke bawah di sisi kiri atau kanan untuk mengubah kecerahan atau volume Tekan dua kali untuk mengubah waktu Tekan dua kali untuk menjeda - Jumlah pengubah waktu pemutar + Jumlah pengubah waktu pemutar (Detik) Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang - Tekan di tengah untuk menjeda + Tekan dua kali di tengah untuk menjeda Gunakan pencerahan sistem Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap Update progres tontonan @@ -149,7 +149,7 @@ Tidak mengirim data Tampilkan episode filler untuk anime Tampilkan update aplikasi - Secara otomatis mencari update terbaru saat aplikasi dibuka + Secara otomatis mencari update terbaru setelah aplikasi dibuka. Update ke prarilis Hanya mencari update prarilis daripada rilis penuh Github @@ -209,8 +209,8 @@ Movie Seri Kartun - \@string/anime - \@string/ova + Anime + OVA Torrent Film Dokumenter Drama Asia @@ -264,7 +264,7 @@ 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 + Tampilkan tombol di halaman utama yang dapat memilih seri film atau TV acak dari halaman utama Bahasa provider Tata Letak Aplikasi Media yang lebih diinginkan @@ -548,7 +548,7 @@ Episode %d telah rilis! raw.githubusercontent.com Proksi Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. - Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Mengunakan jsdelivers, bisa melewati pemblokiran GitHub. Mungkin dapat menyebabkan pembaruan tertunda dalam beberapa hari. Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/app/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index c2561914..dd89c34a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Aflevering Poster Hoofdposter Volgende willekeurig @@ -128,14 +128,14 @@ Eigengravy Modus Voegt een snelheidsoptie toe in de speler Swipe to seek - Veeg naar links of rechts om de tijd in de videoplayer te regelen + Veeg naar links of rechts om de tijd in de videospeler te regelen Veeg om instellingen te wijzigen Veeg naar links of rechts om de helderheid of het volume te wijzigen Dubbeltik om te zien Dubbeltik om te pauzeren - Speler zoeken bedrag + Videospeler aantal zoeken Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken - Tik in het midden om te pauzeren + Tik twee keer in het midden om te pauzeren Systeemhelderheid gebruiken Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay Kijkvoortgang bijwerken @@ -405,4 +405,7 @@ Start de volgende episode wanneer deze afgelopen is Volgende episode automatisch afspelen De update is gestart + Bibliotheek + Browser + Logboek \ 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 0c846361..64ccb903 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -123,16 +123,16 @@ Modo Eigengravy Acrescenta uma opção de velocidade no player Deslize para andar - Deslize para a esq. ou dir. para controlar o tempo no player + Deslize para os lados para controlar a posição em um vídeo Deslize para mudar as configurações - Deslize do lado esq. ou dir. para ajustar brilho ou volume + Deslize para cima ou para baixo, no lado esquerdo ou direito, para ajustar brilho ou volume Reproduzir automaticamente próximo episódio Começa o próximo episódio quando o atual termina Toque duplo para avançar Toque duplo para pôr em pausa - Segundos avançados no player + Tempo de busca no player (Segundos) Toque duplo no lado esq. ou dir. para andar para trás ou para a frente - Toque no meio para pôr em pausa + Toque duas vezes no meio para pausar Usar brilho da sistema Usar brilho do sistema no player em vez de uma sobreposição escura Atualizar progresso @@ -158,7 +158,7 @@ Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa Atualizações de plugin automáticas Mostrar atualizações da app - Procurar novas atualizações automaticamente ao iniciar + Procurar automaticamente por novas atualizações depois de iniciar o app. Atualizar para pré-lançamentos Procura atualizações de pré-lançamento em vez de só lançamentos oficiais Github @@ -273,7 +273,7 @@ Aviso Legal Geral Botão Aleatório - Mostra o botão Aleatório na página inicial + Mostra o botão Aleatório na página inicial, que pode escolher aleatoriamente um filme ou série Idioma dos fornecedores Layout da App Mídia preferida @@ -444,7 +444,7 @@ Cam Abertura Selecionar Biblioteca - Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns dias. + Usando jsdelivr o bloqueio do GitHub pode ser contornado. Pode atrasar atualizações em alguns dias. VLC Todas as linguagens Atualizado (Novo para Antigo) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e613cee4..5295bd35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -142,10 +142,10 @@ Добавляет опцию скорости в проигрывателе Проведите пальцем для поиска Проведите пальцем для изменения настроек - Проведите пальцем по левой или правой стороне для изменения яркости или громкости + Проведите вверх или вниз по левой или правой стороне, чтобы изменить яркость или громкость Автопроиграть следующего серия Поток торрент - Проведите пальцем влево или вправо, чтобы управлять временем в видеоплеере + Проведите пальцем из стороны в сторону, чтобы управлять свое место в видеоролике Начните следующий серию, когда закончится текущий Загружена резервная копия Не удалось восстановить данные из %s @@ -159,7 +159,7 @@ Автоматическое обновление плагинов Автоматическая загрузка плагинов Показать обновления приложения - Автоматически проверять обновления при старте + Автоматически проверять обновления при старте приложения. Обновится до пре-релиза APK установщик Github @@ -227,7 +227,7 @@ Использовано Двойное нажатие для паузы Коснитесь дважды правой или левой стороны для поиска вперед или назад - Нажмите в центре для паузы + Нажмите дважды в центре, чтобы сделать паузу Использовать системную яркость Автоматически синхронизировать текущий прогресс эпизода Ошибка резервного копирования %s @@ -408,8 +408,8 @@ Съешь ещё этих мягких французских булок, да выпей же чаю Рекомендуется Загружено %s - \@нить/аниме - \@нить/ova + Аниме + OVA Этикетка Dub Сайт Функции @@ -493,7 +493,7 @@ Фильтровать по предпочитаемому языку медиа Неверный ID Ссылка на стрим - Отображать рандомную кнопку на Главной странице + Показывает кнопку на главной странице, с помощью которой можно выбрать случайный фильм или сериал с главной страницы Рандомная кнопка Legacy (старый) Веб видеокаст @@ -501,7 +501,7 @@ Перезагрузить ссылки Предпочтительные медиа Опущенные - Объем перемотки плеера + Объем перемотки плеера (секундах) Объем перемотка, используемый, когда плеер виден Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 807716d8..f53bb69d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -117,47 +117,47 @@ Hiç muz verilmedi Otomatik seçilecek dil İndirilecek diller - Alt yazı dili - Varsayılana döndürmek için basılı tut + Altyazı dili + Sıfırlamak için basılı tut Fontları içe aktarmak için %s konumuna yerleştirin İzlemeye devam et Kaldır Daha fazla bilgi \@string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir - Bu sağlayıcı bir torrent. VPN önerilir + Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. Açıklama Konu bulunamadı Açıklama bulunamadı - Logcat\'i göster 🐈 - Resim-içinde-resim - Diğer uygulamaların üzerinde minyatür bir oynatıcıda oynatmaya devam eder + Logcat\'i görüntüle 🐈 + Görüntü içinde görüntü + İçerik diğer uygulamaların üzerinde küçük bir pencerede oynatılmaya devam eder Oynatıcı yeniden boyutlandırma butonu - Siyah sınırları kaldır - Alt yazı + Siyah sınır çizgilerini kaldır + Alt yazılar Oynatıcı alt yazı ayarları - Chromecast alt yazı + Chromecast alt yazıları Chromecast alt yazı ayarları - Eigengravy modu - Oynatıcıya bir hız seçeneği ekle - Gözlemek için kaydır - Zamanı ayarlamak için sağa veya sola kaydır + Eigengrau modu + Oynatıcıya hız seçeneği ekler + Atlamak için kaydır + Zamanı ayarlamak için yanlardan kaydır Ayarları değiştirmek için kaydır - Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla + Sol ve sağ taraftan yukarı kaydırarak ekran parlaklığı ve sesi ayarla Sonraki bölümü otomatik oynat Mevcut bölüm bittiğinde sonraki bölüme başla - Gözlemek için çift tıkla - Durdurmak için çift tıkla - Oynatıcı gözleme miktarı - İleri ve geri atlamak için sağa ve sola çift tıkla - Durdurmak için ortaya tıkla + Çift dokunarak atla + İki kez dokunarak duraklat + Atlanacak süre (Saniye) + İleri ve geri atlamak için sağa ve sola iki kez dokun + Durdurmak için ekranın ortasına çift dokun Sistem parlaklığını kullan - Oynatıcıda karanlık kaplama yerine sistem parlaklığını kullan + Oynatıcıyı karartmak yerine sistem parlaklığını kullan İzleme ilerlemesini güncelle Mevcut bölüm ilerlemesini otomatik güncelle - Yedekten geri yükle - Verileri yedekleyin + Verileri yedekten geri yükle + Verileri yedekle Yedek dosyası yüklendi Geri yükleme başarısız oldu: %s Başarıyla yedeklendi @@ -165,21 +165,21 @@ %s yedeklenirken hata Ara Hesaplar - Güncellemeler ve yedek + Güncellemeler ve yedekleme Bilgi Gelişmiş arama - Sağlayıcılara göre ayrılmış arama sonuçlarını ver + Arama sonuçlarını sağlayıcıya göre ayırır Yalnızca çökmelerle ilgili verileri gönderir - Hiç veri göndermez - Anime için filler bölümleri gösterir + Veri göndermez + Anime için filler bölümleri göster Fragmanları göster Kitsu\'dan posterleri göster - Seçilen video kalitelerini arama sonuçlarında gizle + Seçilen video kalitelerini arama sonuçlarında gösterme Otomatik eklenti güncellemeleri Uygulama güncellemelerini göster - Başlangıçta yeni güncellemeleri otomatik olarak ara - Ön sürümlere güncelle - Sadece tam sürümler yerine ön sürüm güncellemelerini de ara + Uygulama başlatıldıktan sonra güncellemeleri otomatik olarak kontrol et. + Deneysel sürümlere güncelle + Yalnızca tam sürümler yerine deneysel güncellemeleri de ara GitHub Aynı geliştiriciler tarafından LightNovel uygulaması Aynı geliştiriciler tarafından anime uygulaması @@ -191,8 +191,8 @@ Bağlantı bulunamadı Bağlantı panoya kopyalandı Bölümü oynat - Varsayılana sıfırla - Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek + Varsayılan değere sıfırla + Üzgünüz, uygulama çöktü. Geliştiricilere anonim bir hata raporu gönderilecek Sezon %s %d%s Sezon yok @@ -210,8 +210,8 @@ Sürdür -30 +30 - %s dosyası tamamen silinecek -\nEmins misiniz\? + %s tamamen silinecek +\nEmin misiniz\? %dm \nkaldı Devam ediyor @@ -236,9 +236,9 @@ Torrentler Belgeseller OVA - Asya dramaları + Asya dizileri Canlı yayınlar - NSFW + +18 Diğerleri Film @@ -248,9 +248,9 @@ \@string/ova Torrent Belgesel - Asya draması + Asya dizisi Canlı yayın - NSFW + +18 Video Kaynak hatası Sunucu hatası @@ -259,10 +259,10 @@ İndirme hatası, depolama izinlerini kontrol edin Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla - Uygulamada oynat - %s\'deda oynat + Burada oynat + %s üzerinden oynat Tarayıcıda oynat - Linki kopyala + Bağlantıyı kopyala Otomatik indir Şu kaynaktan indir Bağlantıları yenile @@ -281,22 +281,22 @@ Kilitle Yeniden boyutlandır Kaynak - OP\'yi geç + Jeneriği geç Bir daha gösterme Bu güncellemeyi atla Güncelle - Tercih edilen izleme kalitesi - Oynatıcıdaki maksimum başlık karakter sayısı - Oynatıcının üst tarafındaki öğeler + Tercih edilen görüntü kalitesi (WiFi) + Video oynatıcı başlığı karakter üst sınırı + Oynatıcının çözünürlüğü Video arabelleği boyutu Video arabelleği uzunluğu - Diskteki video önbelleği + Hafızadaki video önbelleği Video ve resim önbelleğini temizle - Android TV gibi düşük belleğe sahip cihazlarda çok yükseğe ayarlanırsa çökmelere neden olur. - Çok yükseğe ayarlanırsa, Android TV cihazları gibi düşük depolama alanına sahip sistemlerde sorunlara neden olabilir. - HTTPS üzerinden DNS - ISP bloklarını atlatmak için kullanışlıdır - Klon site + Çok yükseğe ayarlanırsa düşük belleğe sahip cihazlarda çökmelere neden olur (örn. Android TV). + Çok yükseğe ayarlanırsa düşük depolama alanına sahip sistemlerde sorunlara neden olur (örn. Android TV). + HTTPS üzerinden DNS (DoH) + İnternet Servis Sağlayıcısı (İSS) kısıtlamalarını aşmak için kullanışlıdır + Siteyi kopyala Siteyi kaldır Farklı bir URL ile mevcut bir sitenin klonunu ekleyin İndirme konumu @@ -305,16 +305,16 @@ Ekrana sığdır Uzat Yakınlaştır - Disclaimer + 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 butonu - Ana sayfada rastgele butonunu göster + Rastgele İçerik + Ana sayfada rastgele bir film veya dizi seçen bir tuş gösterir Sağlayıcı dilleri Uygulama düzeni Tercih edilen medya - Desteklenen sağlayıcılarda NSFW\'yi etkinleştir + Desteklenen sağlayıcılarda +18 içeriği etkinleştir Alt yazı kodlaması Sağlayıcılar Düzen @@ -336,7 +336,7 @@ hello@world.com 127.0.0.1 MyCoolSite - example.com + ornek.com Dil kodu (tr) Hiçbiri Normal @@ -376,7 +376,7 @@ Alt yazı senkronu 1000 ms Alt yazı gecikmesi - Alt yazılar %d ms erken gözüküyorsa bunu kullanın + Alt yazılar %d ms erken görüntüleniyorsa bunu kullanın Alt yazılar %d ms geç gözüküyorsa bunu kullanın Alt yazı gecikmesi yok Pijamalı hasta yağız şoföre çabucak güvendi Önerilen - %s yüklendi + %s eklendi Dosyadan yükle İnternetten yükle İndirilen dosya @@ -422,10 +422,10 @@ Geçersiz veri Geçersiz URL Hata - Alt yazılardan seçmeli alt yazıyı kaldır + Alt yazılardan seçmeli alt yazıyı (CC) kaldır Alt yazılardaki şişkinliği kaldır Tercih edilen medya diline göre filtrele - Ekstralar + Ek içerikler Fragman Yayına bağlan Yönlendiren @@ -433,7 +433,7 @@ Videoları bu dillerde izle Geri Kurulumu atla - Cihazınıza uygun görünümü seçin + Cihazınıza uygun uygulama görünümünü seçin Çökme raporları Ne izlemek istiyorsunuz Bitti @@ -445,7 +445,7 @@ Eklenti silindi %s yüklenemedi +18 - %d %s … indirilmeye başlandı + %d %s indirilmeye başlandı… %d %s indirildi %s\'nin tamamı zaten indirildi Toplu indir @@ -477,7 +477,7 @@ Çökme bilgisini göster Puan: %s Açıklama - Versiyon + Sürüm Durum Boyut Geliştiriciler @@ -499,14 +499,14 @@ Fragmanı oynat Eklenen depolardan henüz yüklenmemiş tüm eklentileri otomatik olarak yükleyin. Güncelleme başladı - Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemele yüklenmezse eski seçeneği deneyin. + Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemeler yüklenmezse eski seçeneği deneyin. Eklentileri otomatik olarak indir APK indirici - Linkler + Bağlantılar Uygulama güncellemeleri Yedek Oynatıcı özellikleri - Altyazılar + Alt yazılar Düzen Varsayılanlar Eklentiler @@ -531,22 +531,22 @@ İzlenenlerden kaldır Karışık son Karışık başlangıç - Kredi + Katkıda Bulunanlar Giriş Eklenti İndirildi - Aksiyonlar - Açma/bitiş için atlama açılır pencerelerini göster + Eylemler + Açılış/bitiş için atlama açılır pencerelerini göster Çok fazla metin. Panoya kaydedilemiyor. Kütüphane Tarayıcı Görünüşe göre kütüphaneniz boş :( -\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize gösteri ekleyin +\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize içerik ekleyin Güvenli mod dosyası bulundu! \nDosya kaldırılana kadar başlangıçta herhangi bir uzantı yüklenmiyor. Sırala Sırala - Güncel (Yeniden Eskiye) - Güncel (Eskiden Yeniye) + Güncellenme (Yeniden Eskiye) + Güncellenme (Eskiden Yeniye) Alfabetik (A\'dan Z’ye) Alfabetik (Z - A) Kütüphane Seçin @@ -554,4 +554,27 @@ Görünüşe göre bu liste boş, başka bir listeye geçmeyi deneyin Derecelendirme (Yüksekten Düşüğe) Derecelendirme (Düşükten Yükseğe) + Yeniden başlat + Oynatıcı gizlenmişken atlanacak süre + İSS Kısıtlamaları + GitHub\'a ulaşılamadı, jsdelivr vekil sunucusu etkinleştiriliyor. + Başlat + Başarılı oldu + raw.githubusercontent.com vekil sunucusu (proxy) + Tercih edilen görüntü kalitesi (Mobil veri) + Oynatıcı görünürken atlanacak süre + Oynatıcı gizli durumdayken atlanacak süre miktarı + jsdelivr kullanarak GitHub kısıtlamasını aşar. Güncellemeler birkaç gün gecikebilir. + Android TV + Yeni bölüm %d yayınlandı! + Sağlayıcıyı kontrol et + Başarısız oldu + Durdur + Geri al + Abone olunan gösteriler güncelleniyor + Abone olunan + %s kanalına abone olundu + %s kanalı aboneliğinden çıkıldı + Günlük + Oynatıcı görünür durumdayken atlanacak süre miktarı \ 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 a676b583..dc7a452e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,13 +1,13 @@ Постер - Постер епізоду + Постер до епізоду Завантаження скасовано - Змінити постачальника + Змінити провайдера Назад Рейтинг: %.1f Актори: %s - Епізод %d буде випущено через + Епізод %d вийде через Poster %s Еп. %d %dд %dгод %dхв @@ -15,14 +15,14 @@ %dхв Головний постер Наступний випадковий - Перегляд фону + Попередній перегляд фону Швидкість (%.2fx) - Нове оновлення знайдено! + Знайдено нове оновлення! \n%s -> %s Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук %s… Дані відсутні @@ -37,7 +37,7 @@ Покинуто Переглянути фільм Переглянути трейлер - Переглянути торрент + Трансляція через торрент Повторити підключення… Назад Переглянути епізод @@ -64,7 +64,7 @@ Тип контуру Шрифт Розмір шрифту - Пошук за допомогою постачальників + Пошук за допомогою провайдерів Пошук за типами Бананів немає Автовибір мови @@ -75,25 +75,25 @@ Продовжити перегляд Вилучити Детальніше - Цей постачальник є торрентом, рекомендується VPN + Цей провайдер є торрентом, рекомендується VPN Опис Сюжет не знайдено Опис не знайдено Показати Logcat 🐈 - Продовження відтворення в мініатюрному плеєрі поверх інших програм + Продовження відтворення в мініатюрному плеєрі поверх інших застосунків Прибирає чорні рамки Субтитри Субтитри Chromecast Налаштування субтитрів Chromecast Режим Eigengravy Проведіть пальцем, щоб змінити налаштування - Проведіть пальцем ліворуч або праворуч, щоб змінити яскравість або гучність + Проведіть пальцем вгору або вниз ліворуч або праворуч, щоб змінити яскравість або гучність Відтворення наступного епізоду після закінчення поточного Головна CloudStream Філер Програти в CloudStream - Потік + Трансляція Переглядаю Поділитися Відкладено @@ -121,11 +121,11 @@ Колір тексту Колір контуру Автовідтворення наступного епізоду - Проведіть пальцем ліворуч або праворуч, щоб керувати часом у відеоплеєрі + Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play - Для коректної роботи цього постачальника може знадобитися VPN + Для коректної роботи цього провайдера може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці Налаштування субтитрів плеєра @@ -133,8 +133,8 @@ Проведіть пальцем, щоб перемотати Двічі торкніться, щоб перемотати Двічі торкніться для паузи - Крок перемотки - Натисніть посередині, щоб поставити на паузу + Крок перемотки (Секунди) + Натисніть двічі посередині, щоб призупинити Використовувати яскравість системи Оновити прогрес перегляду Відновлення даних з резервної копії @@ -147,23 +147,23 @@ Оновлення та резервне копіювання Інформація Розширений пошук - Надає результати пошуку, розділені за постачальниками + Надає результати пошуку, розділені за провайдерами Надсилає дані лише про збої Не надсилає даних - Показати заповнюючий епізод для аніме + Показати філерний епізод для аніме Показати трейлери Приховати вибрану якість відео в результатах пошуку Автоматичне завантаження плагінів - Показати оновлення програми + Показати оновлення застосунку Повторний процес налаштування - Пошук лише попередніх оновлень, а не повних релізів + Пошук лише бета-оновлень, а не повних релізів Встановлювач APK Github Застосунок для легких новел від тих же розробників Застосунок для аніме від тих же розробників Дайте бананів розробникам - Мова програми - Цей постачальник не має підтримки Chromecast + Мова застосунку + Цей провайдер не має підтримки Chromecast Посилань не знайдено Переглянути епізод Скинути до значення за замовчуванням @@ -180,7 +180,7 @@ \nВи впевнені\? %dхв \nзалишилося - Триває + Виходить Завершено Рейтинг Тривалість @@ -189,7 +189,7 @@ За замовчуванням Вільно Зайнято - Програма + Застосунок Телесеріали Мультфільми Аніме @@ -208,7 +208,7 @@ Віддалена помилка Помилка рендеринга Дзеркало Chromecast - Переглянути в програмі + Переглянути в застосунку Переглянути в %s Автозавантаження Завантажити дзеркало @@ -230,7 +230,7 @@ Показати постери від Kitsu Автоматичне оновлення плагінів Автоматично встановлювати всі ще не встановлені плагіни з доданих репозиторіїв. - Автоматичний пошук нових оновлень при запуску + Автоматично шукати нові оновлення після запуску застосунку. Оновлення до бета-версій Посилання скопійовано в буфер обміну Деякі телефони не підтримують новий інсталятор пакетів. Спробуйте стару версію, якщо оновлення не встановлюються. @@ -255,7 +255,7 @@ Документальні фільми NSFW Фільм - \@string/ova + OVA Торрент Мітка якості NSFW @@ -273,7 +273,7 @@ Заповнити Збільшити Доріжки - Оновлення програми + Оновлення застосунку Кеш Жести Особливості плеєра @@ -283,16 +283,16 @@ Особливості Загальне Випадкова кнопка - Показати випадкову кнопку на Головній сторінці - Мови постачальника - Макет програми + Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці + Мови провайдера + Макет застосунку Бажані медіа Авто Макет телевізора Макет телефону Макет емулятора Основний колір - Тема програми + Тема застосунку Розташування назви постера Розмістіть назву під постером пароль123 @@ -363,7 +363,7 @@ Кодування субтитрів Включити NSFW на підтримуваних постачальників Макет - Постачальники + Провайдери example.com %s %s Депресивний @@ -429,7 +429,7 @@ Оновлено %d плагіни За замовчуванням в CloudStream не встановлені сайти. Вам потрібно встановити сайти з репозиторіїв. \n -\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунку. +\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунок. \n \nПриєднуйтесь до нашого Discord або шукайте в інтернеті. Переглянути репозиторії спільноти @@ -451,28 +451,28 @@ Вбудований плеєр VLC MPV - Відтворення веб-відео - Веб-браузер - Кінець + Відтворення вебвідео + Веббраузер + Ендінґ Коротке повторення Пропустити %s - Змішаний кінець + Змішаний ендінґ Подяки - Опенінг + Опенінґ Вступ Очистити історію Історія - Показувати спливаючі вікна для опенінгу/кінця + Показувати спливаючі вікна для опенінґу/ендінґу Забагато тексту. Не вдалося зберегти в буфер обміну. Позначити як переглянуте Ви впевнені що хочете вийти\? Так Ні - Установлення оновлення програми… - Не вдалося встановити нову версію програми + Встановлення оновлення застосунку… + Не вдалося встановити нову версію застосунку Старий - Інсталятор пакетів - Програму буде оновлено після виходу + Встановлювач пакетів + Застосунок буде оновлено після виходу Це також призведе до видалення всіх плагінів репозиторію Всі мови Назад @@ -484,10 +484,10 @@ Бажаний відеоплеєр Увімкнено безпечний режим Автори - Завантаження оновлення програми… + Завантаження оновлення застосунку… Усі розширення вимкнено через збій, щоб допомогти вам знайти те, що спричиняє проблеми. - Програму не знайдено - Змішаний опенінг + Застосунок не знайдено + Змішаний опенінґ Видалити з переглянутого За оновленням (від старого до нового) За оновленням (від нового до старого) @@ -517,17 +517,16 @@ Журнал Старт Стоп - Тест постачальника + Тест провайдер Оновлення підписаних шоу Підписано Підписано на %s Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com -\nProxy + raw.githubusercontent.com Проксі Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP - Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 72d62a04..a14b87cc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -142,14 +142,14 @@ 倍速模式 在播放器中添加播放速度选项 滑动控制进度 - 左右滑动控制播放进度 + 左右滑动以控制视频中的位置 滑动更改设置 上下滑动修改亮度或音量 自动播放下一集 播放完毕后播放下一集 双击控制进度 双击暂停 - 双击控制进度时间 + 双击控制进度时间 (秒) 在左右侧双击快进或快退 双击中间暂停 使用系统亮度 @@ -178,7 +178,7 @@ 自动更新插件 自动下载插件 显示应用更新 - 启动时自动搜索更新 + 启动应用后自动搜索更新。 更新至预览版 搜索预览版更新替代仅搜索完整版本 Github @@ -245,8 +245,8 @@ 电影 电视剧 卡通 - \@string/anime - \@string/ova + 动漫 + OVA 种子 纪录片 亚洲剧 @@ -311,7 +311,7 @@ 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. 通用 随机按钮 - 在主页中显示随机按钮 + 在主页上显示按钮,可以从主页上随机选择电影或电视剧 片源语言 应用布局 首选类型 @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49380b5e..911c0d07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,17 +210,17 @@ Eigengravy Mode Adds a speed option in the player Swipe to seek - Swipe left or right to control time in the videoplayer + Swipe from side to side to control your position in a video Swipe to change settings - Swipe on the left or right side to change brightness or volume + Slide up or down on the left or right side to change brightness or volume Autoplay next episode Start the next episode when the current one ends Double tap to seek Double tap to pause - Player seek amount + Player seek amount (Seconds) Tap twice on the right or left side to seek forwards or backwards - Tap in the middle to pause + Tap twice in the middle to pause Use system brightness Use system brightness in the app player instead of a dark overlay @@ -251,7 +251,7 @@ Automatically download plugins Automatically install all not yet installed plugins from added repositories. Show app updates - Automatically search for new updates on start + Automatically search for new updates after starting the app. Redo setup process Update to prereleases Search for prerelease updates instead of full releases only @@ -324,8 +324,8 @@ Movie Series Cartoon - @string/anime - @string/ova + Anime + OVA Torrent Documentary Asian Drama @@ -383,7 +383,7 @@ Useful for bypassing ISP blocks raw.githubusercontent.com Proxy Failed to reach GitHub, enabling jsdelivr proxy. - Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. + Using jsdelivr, GitHub blocking can be bypassed. May delay updates by a few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -428,7 +428,7 @@ Features General Random Button - Show random button on Homepage + Shows button on Homepage which can choose a random movie or TV series from the Homepage Provider languages App Layout Preferred media @@ -657,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - + \ No newline at end of file From fab55d82c480c2de7a630b567c94e44fbc30ac41 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 9 Mar 2023 22:35:44 +0100 Subject: [PATCH 255/273] Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 18 +-- app/src/main/res/values-cs/strings.xml | 18 +-- app/src/main/res/values-es/strings.xml | 18 +-- app/src/main/res/values-in/strings.xml | 18 +-- app/src/main/res/values-ms/strings.xml | 2 + app/src/main/res/values-nl/strings.xml | 11 +- app/src/main/res/values-pt/strings.xml | 14 +- app/src/main/res/values-ru/strings.xml | 16 +-- app/src/main/res/values-tr/strings.xml | 173 ++++++++++++++----------- app/src/main/res/values-uk/strings.xml | 33 +++-- app/src/main/res/values-zh/strings.xml | 14 +- app/src/main/res/values/strings.xml | 20 +-- 12 files changed, 191 insertions(+), 164 deletions(-) create mode 100644 app/src/main/res/values-ms/strings.xml diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index cfd761e3..ae45465b 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -119,16 +119,16 @@ وضع إيغنغرافي يضيف خيار السرعة في المُشغل السحب لتقديم - إسحب إلى اليسار أو اليمين للتحكم في الوقت في مُشغل الفيديو + اسحب من جانب إلى آخر للتحكم في موضعك في مقطع فيديو السحب لتغيير الإعدادات - إسحب على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت + مرر لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت تشغيل الحلقة التالية تلقائيًا تبدأ الحلقة التالية عندما تنتهي الحالية النقر مرتان للتقديم للأمام أو للخلف الضغط مرتان لإيقاف مؤقت - التحكم في مدى تقديم المُشغل + التحكم في مدى تقديم المُشغل(ثوان) إضغط مرتين على الجانب الأيمن أو الأيسر للتقديم للأمام أو للخلف - إضغط في الوسط لإيقاف مؤقت + اضغط مرتين في المنتصف للتوقف استخدم سطوع النظام استخدم سطوع النظام في مُشغل التطبيق بدلاً من التراكب الداكن تحديث تقدم المشاهدة @@ -155,7 +155,7 @@ تحديث الإضافات تلقائيًا تنزيل الإضافات تلقائيًا التحديث التلقائي - البحث تلقائيًا عن التحديثات الجديدة عند البداية + ابحث تلقائيا عن التحديثات الجديدة بعد بدء التطبيق. التحديث إلى الاصدارات التجريبية (بيتا) البحث عن التحديثات التجريبية بدلاً من الإصدارات الكاملة فقط غيت هاب @@ -218,8 +218,8 @@ فيلم مسلسل كرتون - أنمي - اوفا + أنيمي + أوفا تورنت وثائقي دراما آسيوية @@ -284,7 +284,7 @@ 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. عام زر العشوائي - إظهار زر العشوائي على الصفحة الرئيسية + يظهر الزر على الصفحة الرئيسية والذي يمكنه اختيار فيلم عشوائي أو مسلسل تلفزيوني من الصفحة الرئيسية لغات المزود واجهة التطبيق المحتوى المفضل @@ -558,7 +558,7 @@ تجاوز مزود خدمة الإنترنت استرجاع فشل الوصول إلى GitHub ، وتمكين وكيل jsdelivr. - تجاوز حظر GitHub باستخدام jsdelivr ، قد يتسبب في تأخير التحديثات لبضعة أيام. + باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) \ 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 e99e1010..67179b46 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -122,14 +122,14 @@ Rychlostní režim Přidá do přehrávače možnost rychlosti Přejet pro posun - Přejeďte prstem vlevo nebo vpravo pro ovládání času v přehrávači + Přejeďte prstem ze strany na stranu pro ovládání své pozice ve videu Přejet pro změnu nastavení - Přejeďte prstem na levé nebo pravé straně pro změnu jasu nebo hlasitosti + Přejeďte prstem nahoru nebo dolů na levé nebo pravé straně pro změnu jasu nebo hlasitosti Dvojité klepnutí pro posun Dvojité klepnutí pro pozastavení - Množství času k posunu + Množství času k posunu (sekundy) Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad - Klepněte doprostřed pro pozastavení + Klepněte dvakrát doprostřed pro pozastavení Použít systémový jas V přehrávači použít systémov překrytí Aktualizovat postup sledování @@ -151,7 +151,7 @@ Nebude odesílat žádná data Zobrazit výplňové epizody u anime Zobrazit aktualizace aplikace - Při spuštění automaticky zkontrolovat nové aktualizace + Při spuštění aplikace automaticky zkontrolovat nové aktualizace. Aktualizovat na předběžná vydání Kontrolovat aktualizace předběžných vydání, místo normálních plných vydání GitHub @@ -211,8 +211,8 @@ Film Seriál Animovaný - \@string/anime - \@string/ova + Anime + OVA Torrent Dokument Asijské drama @@ -266,7 +266,7 @@ Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí. Obecné Náhodné tlačítko - Zobrazit na domovské stránce náhodné tlačítko + Zobrazit na domovské stránce tlačítko, kterým lze vybrat náhodný film nebo seriál z domovské stránky Jazyk poskytovatelů Rozložení aplikace Preferovaná média @@ -551,6 +551,6 @@ Nepodařilo se připojit ke GitHubu, povolování proxy jsdelivr. Upřednostněná kvalita sledování (mobilní data) Vrátit zpět - Obchází blokování GitHubu pomocí jsdelivr, může způsobit zpoždění aktualizací o několik dní. + Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP \ 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 0b195275..5c8ac532 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -51,10 +51,10 @@ Elevado Use esto si los subtítulos se muestran %d ms muy pronto Use esto si los subtítulos se muestran %d ms tarde - Desliza el dedo hacia la izquierda o hacia la derecha para controlar el tiempo en el reproductor de video + Desliza el dedo de lado a lado para controlar la posición en un video Filtrar por idioma de medios preferido Eliminar Closed Captions (CC) de los subtítulos - Cantidad de tiempo de búsqueda en el reproductor (en segundos) + 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 @@ -205,16 +205,16 @@ Modo Eigengravy Deslice para avanzar/retroceder Deslice para cambiar la configuración - Deslice el dedo hacia la izquierda o hacia la derecha para cambiar el brillo o el volumen + Deslice hacia arriba o hacia abajo en el lado izquierdo o derecho para cambiar el brillo o el volumen Toca dos veces para buscar Tocar dos veces para pausar Toque dos veces en el lado derecho o izquierdo para buscar hacia adelante o hacia atrás - Toque en el medio para pausar + Toque dos veces en el medio para hacer una pausa Usar brillo del sistema Restaurar datos desde el backup Hacer copia de los datos (backup) Archivo de backup cargado - Buscar automáticamente nuevas actualizaciones al inicio + Busque automáticamente nuevas actualizaciones después de iniciar la aplicación. Rehacer el proceso de configuración inicial Mostrar episodio de relleno para Anime Reproducir Episodio @@ -306,7 +306,7 @@ Aspecto Características Botón de Al azar - Muestra un botón de reproducción \"al azar\" en la página de inicio + Muestra un botón de reproducción \"al azar\" en la página de inicio para poelículas y series cuenta Cerrar sesión Cambiar cuenta @@ -363,8 +363,8 @@ Película Serie Dibujo animado - \@string/anime - \@string/ova + Anime + OVA Torrent Documental Drama asiático @@ -525,7 +525,7 @@ ¡Episodio %d publicado! Proxy raw.githubusercontent.com No se ha podido acceder a GitHub, activando el proxy jsdelivr. - Evita el bloqueo de GitHub usando jsdelivr, puede causar que las actualizaciones se retrasen unos días. + Con jsdelivr, se puede omitir el bloqueo de GitHub. Puede retrasar las actualizaciones unos días. Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 0e383562..1913868a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -120,14 +120,14 @@ Mode Eigengravy Menambahkan opsi kecepatan di pemutar Geser untuk mengubah waktu - Geser ke kiri atau kanan untuk mengontrol waktu di pemutar video + Geser dari sisi ke sisi untuk mengontrol posisi dalam video Geser untuk mengubah pengaturan - Geser ke sisi kiri atau kanan untuk mengubah pencerahan atau volume + Geser ke atas atau ke bawah di sisi kiri atau kanan untuk mengubah kecerahan atau volume Tekan dua kali untuk mengubah waktu Tekan dua kali untuk menjeda - Jumlah pengubah waktu pemutar + Jumlah pengubah waktu pemutar (Detik) Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang - Tekan di tengah untuk menjeda + Tekan dua kali di tengah untuk menjeda Gunakan pencerahan sistem Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap Update progres tontonan @@ -149,7 +149,7 @@ Tidak mengirim data Tampilkan episode filler untuk anime Tampilkan update aplikasi - Secara otomatis mencari update terbaru saat aplikasi dibuka + Secara otomatis mencari update terbaru setelah aplikasi dibuka. Update ke prarilis Hanya mencari update prarilis daripada rilis penuh Github @@ -209,8 +209,8 @@ Movie Seri Kartun - \@string/anime - \@string/ova + Anime + OVA Torrent Film Dokumenter Drama Asia @@ -264,7 +264,7 @@ 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 + Tampilkan tombol di halaman utama yang dapat memilih seri film atau TV acak dari halaman utama Bahasa provider Tata Letak Aplikasi Media yang lebih diinginkan @@ -548,7 +548,7 @@ Episode %d telah rilis! raw.githubusercontent.com Proksi Gagal mencapai GitHub, mengaktifkan proksi jsdelivr. - Bypass pemblokiran Github menggunakan JSDeliVR, dapat menyebabkan pembaruan tertunda beberapa hari. + Mengunakan jsdelivers, bisa melewati pemblokiran GitHub. Mungkin dapat menyebabkan pembaruan tertunda dalam beberapa hari. Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml new file mode 100644 index 00000000..a6b3daec --- /dev/null +++ b/app/src/main/res/values-ms/strings.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index c2561914..dd89c34a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,7 @@ %dm Poster - \@string/result_poster_img_des + Poster Aflevering Poster Hoofdposter Volgende willekeurig @@ -128,14 +128,14 @@ Eigengravy Modus Voegt een snelheidsoptie toe in de speler Swipe to seek - Veeg naar links of rechts om de tijd in de videoplayer te regelen + Veeg naar links of rechts om de tijd in de videospeler te regelen Veeg om instellingen te wijzigen Veeg naar links of rechts om de helderheid of het volume te wijzigen Dubbeltik om te zien Dubbeltik om te pauzeren - Speler zoeken bedrag + Videospeler aantal zoeken Tik twee keer aan de rechter- of linkerkant om vooruit of achteruit te zoeken - Tik in het midden om te pauzeren + Tik twee keer in het midden om te pauzeren Systeemhelderheid gebruiken Gebruik systeemhelderheid in de app-speler in plaats van een donkere overlay Kijkvoortgang bijwerken @@ -405,4 +405,7 @@ Start de volgende episode wanneer deze afgelopen is Volgende episode automatisch afspelen De update is gestart + Bibliotheek + Browser + Logboek \ 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 0c846361..64ccb903 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -123,16 +123,16 @@ Modo Eigengravy Acrescenta uma opção de velocidade no player Deslize para andar - Deslize para a esq. ou dir. para controlar o tempo no player + Deslize para os lados para controlar a posição em um vídeo Deslize para mudar as configurações - Deslize do lado esq. ou dir. para ajustar brilho ou volume + Deslize para cima ou para baixo, no lado esquerdo ou direito, para ajustar brilho ou volume Reproduzir automaticamente próximo episódio Começa o próximo episódio quando o atual termina Toque duplo para avançar Toque duplo para pôr em pausa - Segundos avançados no player + Tempo de busca no player (Segundos) Toque duplo no lado esq. ou dir. para andar para trás ou para a frente - Toque no meio para pôr em pausa + Toque duas vezes no meio para pausar Usar brilho da sistema Usar brilho do sistema no player em vez de uma sobreposição escura Atualizar progresso @@ -158,7 +158,7 @@ Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa Atualizações de plugin automáticas Mostrar atualizações da app - Procurar novas atualizações automaticamente ao iniciar + Procurar automaticamente por novas atualizações depois de iniciar o app. Atualizar para pré-lançamentos Procura atualizações de pré-lançamento em vez de só lançamentos oficiais Github @@ -273,7 +273,7 @@ Aviso Legal Geral Botão Aleatório - Mostra o botão Aleatório na página inicial + Mostra o botão Aleatório na página inicial, que pode escolher aleatoriamente um filme ou série Idioma dos fornecedores Layout da App Mídia preferida @@ -444,7 +444,7 @@ Cam Abertura Selecionar Biblioteca - Contorna o bloqueio do GitHub ao usar jsdelivr, pode atrasar atualizações em alguns dias. + Usando jsdelivr o bloqueio do GitHub pode ser contornado. Pode atrasar atualizações em alguns dias. VLC Todas as linguagens Atualizado (Novo para Antigo) diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e613cee4..5295bd35 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -142,10 +142,10 @@ Добавляет опцию скорости в проигрывателе Проведите пальцем для поиска Проведите пальцем для изменения настроек - Проведите пальцем по левой или правой стороне для изменения яркости или громкости + Проведите вверх или вниз по левой или правой стороне, чтобы изменить яркость или громкость Автопроиграть следующего серия Поток торрент - Проведите пальцем влево или вправо, чтобы управлять временем в видеоплеере + Проведите пальцем из стороны в сторону, чтобы управлять свое место в видеоролике Начните следующий серию, когда закончится текущий Загружена резервная копия Не удалось восстановить данные из %s @@ -159,7 +159,7 @@ Автоматическое обновление плагинов Автоматическая загрузка плагинов Показать обновления приложения - Автоматически проверять обновления при старте + Автоматически проверять обновления при старте приложения. Обновится до пре-релиза APK установщик Github @@ -227,7 +227,7 @@ Использовано Двойное нажатие для паузы Коснитесь дважды правой или левой стороны для поиска вперед или назад - Нажмите в центре для паузы + Нажмите дважды в центре, чтобы сделать паузу Использовать системную яркость Автоматически синхронизировать текущий прогресс эпизода Ошибка резервного копирования %s @@ -408,8 +408,8 @@ Съешь ещё этих мягких французских булок, да выпей же чаю Рекомендуется Загружено %s - \@нить/аниме - \@нить/ova + Аниме + OVA Этикетка Dub Сайт Функции @@ -493,7 +493,7 @@ Фильтровать по предпочитаемому языку медиа Неверный ID Ссылка на стрим - Отображать рандомную кнопку на Главной странице + Показывает кнопку на главной странице, с помощью которой можно выбрать случайный фильм или сериал с главной страницы Рандомная кнопка Legacy (старый) Веб видеокаст @@ -501,7 +501,7 @@ Перезагрузить ссылки Предпочтительные медиа Опущенные - Объем перемотки плеера + Объем перемотки плеера (секундах) Объем перемотка, используемый, когда плеер виден Плеер показан - Перемотки объем Плеер спрятан - Перемотки объем diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 807716d8..f53bb69d 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -117,47 +117,47 @@ Hiç muz verilmedi Otomatik seçilecek dil İndirilecek diller - Alt yazı dili - Varsayılana döndürmek için basılı tut + Altyazı dili + Sıfırlamak için basılı tut Fontları içe aktarmak için %s konumuna yerleştirin İzlemeye devam et Kaldır Daha fazla bilgi \@string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir - Bu sağlayıcı bir torrent. VPN önerilir + Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. Açıklama Konu bulunamadı Açıklama bulunamadı - Logcat\'i göster 🐈 - Resim-içinde-resim - Diğer uygulamaların üzerinde minyatür bir oynatıcıda oynatmaya devam eder + Logcat\'i görüntüle 🐈 + Görüntü içinde görüntü + İçerik diğer uygulamaların üzerinde küçük bir pencerede oynatılmaya devam eder Oynatıcı yeniden boyutlandırma butonu - Siyah sınırları kaldır - Alt yazı + Siyah sınır çizgilerini kaldır + Alt yazılar Oynatıcı alt yazı ayarları - Chromecast alt yazı + Chromecast alt yazıları Chromecast alt yazı ayarları - Eigengravy modu - Oynatıcıya bir hız seçeneği ekle - Gözlemek için kaydır - Zamanı ayarlamak için sağa veya sola kaydır + Eigengrau modu + Oynatıcıya hız seçeneği ekler + Atlamak için kaydır + Zamanı ayarlamak için yanlardan kaydır Ayarları değiştirmek için kaydır - Sol ve sağ taraftan kaydırarak parlaklık ve sesi ayarla + Sol ve sağ taraftan yukarı kaydırarak ekran parlaklığı ve sesi ayarla Sonraki bölümü otomatik oynat Mevcut bölüm bittiğinde sonraki bölüme başla - Gözlemek için çift tıkla - Durdurmak için çift tıkla - Oynatıcı gözleme miktarı - İleri ve geri atlamak için sağa ve sola çift tıkla - Durdurmak için ortaya tıkla + Çift dokunarak atla + İki kez dokunarak duraklat + Atlanacak süre (Saniye) + İleri ve geri atlamak için sağa ve sola iki kez dokun + Durdurmak için ekranın ortasına çift dokun Sistem parlaklığını kullan - Oynatıcıda karanlık kaplama yerine sistem parlaklığını kullan + Oynatıcıyı karartmak yerine sistem parlaklığını kullan İzleme ilerlemesini güncelle Mevcut bölüm ilerlemesini otomatik güncelle - Yedekten geri yükle - Verileri yedekleyin + Verileri yedekten geri yükle + Verileri yedekle Yedek dosyası yüklendi Geri yükleme başarısız oldu: %s Başarıyla yedeklendi @@ -165,21 +165,21 @@ %s yedeklenirken hata Ara Hesaplar - Güncellemeler ve yedek + Güncellemeler ve yedekleme Bilgi Gelişmiş arama - Sağlayıcılara göre ayrılmış arama sonuçlarını ver + Arama sonuçlarını sağlayıcıya göre ayırır Yalnızca çökmelerle ilgili verileri gönderir - Hiç veri göndermez - Anime için filler bölümleri gösterir + Veri göndermez + Anime için filler bölümleri göster Fragmanları göster Kitsu\'dan posterleri göster - Seçilen video kalitelerini arama sonuçlarında gizle + Seçilen video kalitelerini arama sonuçlarında gösterme Otomatik eklenti güncellemeleri Uygulama güncellemelerini göster - Başlangıçta yeni güncellemeleri otomatik olarak ara - Ön sürümlere güncelle - Sadece tam sürümler yerine ön sürüm güncellemelerini de ara + Uygulama başlatıldıktan sonra güncellemeleri otomatik olarak kontrol et. + Deneysel sürümlere güncelle + Yalnızca tam sürümler yerine deneysel güncellemeleri de ara GitHub Aynı geliştiriciler tarafından LightNovel uygulaması Aynı geliştiriciler tarafından anime uygulaması @@ -191,8 +191,8 @@ Bağlantı bulunamadı Bağlantı panoya kopyalandı Bölümü oynat - Varsayılana sıfırla - Üzgünüz, uygulama çöktü. Geliştiricilere isimsiz bir hata raporu gönderilecek + Varsayılan değere sıfırla + Üzgünüz, uygulama çöktü. Geliştiricilere anonim bir hata raporu gönderilecek Sezon %s %d%s Sezon yok @@ -210,8 +210,8 @@ Sürdür -30 +30 - %s dosyası tamamen silinecek -\nEmins misiniz\? + %s tamamen silinecek +\nEmin misiniz\? %dm \nkaldı Devam ediyor @@ -236,9 +236,9 @@ Torrentler Belgeseller OVA - Asya dramaları + Asya dizileri Canlı yayınlar - NSFW + +18 Diğerleri Film @@ -248,9 +248,9 @@ \@string/ova Torrent Belgesel - Asya draması + Asya dizisi Canlı yayın - NSFW + +18 Video Kaynak hatası Sunucu hatası @@ -259,10 +259,10 @@ İndirme hatası, depolama izinlerini kontrol edin Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla - Uygulamada oynat - %s\'deda oynat + Burada oynat + %s üzerinden oynat Tarayıcıda oynat - Linki kopyala + Bağlantıyı kopyala Otomatik indir Şu kaynaktan indir Bağlantıları yenile @@ -281,22 +281,22 @@ Kilitle Yeniden boyutlandır Kaynak - OP\'yi geç + Jeneriği geç Bir daha gösterme Bu güncellemeyi atla Güncelle - Tercih edilen izleme kalitesi - Oynatıcıdaki maksimum başlık karakter sayısı - Oynatıcının üst tarafındaki öğeler + Tercih edilen görüntü kalitesi (WiFi) + Video oynatıcı başlığı karakter üst sınırı + Oynatıcının çözünürlüğü Video arabelleği boyutu Video arabelleği uzunluğu - Diskteki video önbelleği + Hafızadaki video önbelleği Video ve resim önbelleğini temizle - Android TV gibi düşük belleğe sahip cihazlarda çok yükseğe ayarlanırsa çökmelere neden olur. - Çok yükseğe ayarlanırsa, Android TV cihazları gibi düşük depolama alanına sahip sistemlerde sorunlara neden olabilir. - HTTPS üzerinden DNS - ISP bloklarını atlatmak için kullanışlıdır - Klon site + Çok yükseğe ayarlanırsa düşük belleğe sahip cihazlarda çökmelere neden olur (örn. Android TV). + Çok yükseğe ayarlanırsa düşük depolama alanına sahip sistemlerde sorunlara neden olur (örn. Android TV). + HTTPS üzerinden DNS (DoH) + İnternet Servis Sağlayıcısı (İSS) kısıtlamalarını aşmak için kullanışlıdır + Siteyi kopyala Siteyi kaldır Farklı bir URL ile mevcut bir sitenin klonunu ekleyin İndirme konumu @@ -305,16 +305,16 @@ Ekrana sığdır Uzat Yakınlaştır - Disclaimer + 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 butonu - Ana sayfada rastgele butonunu göster + Rastgele İçerik + Ana sayfada rastgele bir film veya dizi seçen bir tuş gösterir Sağlayıcı dilleri Uygulama düzeni Tercih edilen medya - Desteklenen sağlayıcılarda NSFW\'yi etkinleştir + Desteklenen sağlayıcılarda +18 içeriği etkinleştir Alt yazı kodlaması Sağlayıcılar Düzen @@ -336,7 +336,7 @@ hello@world.com 127.0.0.1 MyCoolSite - example.com + ornek.com Dil kodu (tr) Hiçbiri Normal @@ -376,7 +376,7 @@ Alt yazı senkronu 1000 ms Alt yazı gecikmesi - Alt yazılar %d ms erken gözüküyorsa bunu kullanın + Alt yazılar %d ms erken görüntüleniyorsa bunu kullanın Alt yazılar %d ms geç gözüküyorsa bunu kullanın Alt yazı gecikmesi yok Pijamalı hasta yağız şoföre çabucak güvendi Önerilen - %s yüklendi + %s eklendi Dosyadan yükle İnternetten yükle İndirilen dosya @@ -422,10 +422,10 @@ Geçersiz veri Geçersiz URL Hata - Alt yazılardan seçmeli alt yazıyı kaldır + Alt yazılardan seçmeli alt yazıyı (CC) kaldır Alt yazılardaki şişkinliği kaldır Tercih edilen medya diline göre filtrele - Ekstralar + Ek içerikler Fragman Yayına bağlan Yönlendiren @@ -433,7 +433,7 @@ Videoları bu dillerde izle Geri Kurulumu atla - Cihazınıza uygun görünümü seçin + Cihazınıza uygun uygulama görünümünü seçin Çökme raporları Ne izlemek istiyorsunuz Bitti @@ -445,7 +445,7 @@ Eklenti silindi %s yüklenemedi +18 - %d %s … indirilmeye başlandı + %d %s indirilmeye başlandı… %d %s indirildi %s\'nin tamamı zaten indirildi Toplu indir @@ -477,7 +477,7 @@ Çökme bilgisini göster Puan: %s Açıklama - Versiyon + Sürüm Durum Boyut Geliştiriciler @@ -499,14 +499,14 @@ Fragmanı oynat Eklenen depolardan henüz yüklenmemiş tüm eklentileri otomatik olarak yükleyin. Güncelleme başladı - Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemele yüklenmezse eski seçeneği deneyin. + Bazı cihazlar yeni paket yükleyiciyi desteklemez.. Güncellemeler yüklenmezse eski seçeneği deneyin. Eklentileri otomatik olarak indir APK indirici - Linkler + Bağlantılar Uygulama güncellemeleri Yedek Oynatıcı özellikleri - Altyazılar + Alt yazılar Düzen Varsayılanlar Eklentiler @@ -531,22 +531,22 @@ İzlenenlerden kaldır Karışık son Karışık başlangıç - Kredi + Katkıda Bulunanlar Giriş Eklenti İndirildi - Aksiyonlar - Açma/bitiş için atlama açılır pencerelerini göster + Eylemler + Açılış/bitiş için atlama açılır pencerelerini göster Çok fazla metin. Panoya kaydedilemiyor. Kütüphane Tarayıcı Görünüşe göre kütüphaneniz boş :( -\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize gösteri ekleyin +\nBir kütüphane hesabına giriş yapın veya yerel kütüphanenize içerik ekleyin Güvenli mod dosyası bulundu! \nDosya kaldırılana kadar başlangıçta herhangi bir uzantı yüklenmiyor. Sırala Sırala - Güncel (Yeniden Eskiye) - Güncel (Eskiden Yeniye) + Güncellenme (Yeniden Eskiye) + Güncellenme (Eskiden Yeniye) Alfabetik (A\'dan Z’ye) Alfabetik (Z - A) Kütüphane Seçin @@ -554,4 +554,27 @@ Görünüşe göre bu liste boş, başka bir listeye geçmeyi deneyin Derecelendirme (Yüksekten Düşüğe) Derecelendirme (Düşükten Yükseğe) + Yeniden başlat + Oynatıcı gizlenmişken atlanacak süre + İSS Kısıtlamaları + GitHub\'a ulaşılamadı, jsdelivr vekil sunucusu etkinleştiriliyor. + Başlat + Başarılı oldu + raw.githubusercontent.com vekil sunucusu (proxy) + Tercih edilen görüntü kalitesi (Mobil veri) + Oynatıcı görünürken atlanacak süre + Oynatıcı gizli durumdayken atlanacak süre miktarı + jsdelivr kullanarak GitHub kısıtlamasını aşar. Güncellemeler birkaç gün gecikebilir. + Android TV + Yeni bölüm %d yayınlandı! + Sağlayıcıyı kontrol et + Başarısız oldu + Durdur + Geri al + Abone olunan gösteriler güncelleniyor + Abone olunan + %s kanalına abone olundu + %s kanalı aboneliğinden çıkıldı + Günlük + Oynatıcı görünür durumdayken atlanacak süre miktarı \ 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 a676b583..6dca29b4 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,13 +1,13 @@ Постер - Постер епізоду + Постер до епізоду Завантаження скасовано Змінити постачальника Назад Рейтинг: %.1f Актори: %s - Епізод %d буде випущено через + Епізод %d вийде через Poster %s Еп. %d %dд %dгод %dхв @@ -15,16 +15,16 @@ %dхв Головний постер Наступний випадковий - Перегляд фону + Попередній перегляд фону Швидкість (%.2fx) - Нове оновлення знайдено! + Знайдено нове оновлення! \n%s -> %s Пошук Завантаження %d хв Параметри Пошук… - Пошук %s… + Пошук на %s… Дані відсутні Більше опцій Наступний епізод @@ -37,7 +37,7 @@ Покинуто Переглянути фільм Переглянути трейлер - Переглянути торрент + Трансляція через торрент Повторити підключення… Назад Переглянути епізод @@ -87,13 +87,13 @@ Налаштування субтитрів Chromecast Режим Eigengravy Проведіть пальцем, щоб змінити налаштування - Проведіть пальцем ліворуч або праворуч, щоб змінити яскравість або гучність + Проведіть пальцем вгору або вниз ліворуч або праворуч, щоб змінити яскравість або гучність Відтворення наступного епізоду після закінчення поточного Головна CloudStream Філер Програти в CloudStream - Потік + Трансляція Переглядаю Поділитися Відкладено @@ -121,7 +121,7 @@ Колір тексту Колір контуру Автовідтворення наступного епізоду - Проведіть пальцем ліворуч або праворуч, щоб керувати часом у відеоплеєрі + Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play @@ -133,8 +133,8 @@ Проведіть пальцем, щоб перемотати Двічі торкніться, щоб перемотати Двічі торкніться для паузи - Крок перемотки - Натисніть посередині, щоб поставити на паузу + Крок перемотки (Секунди) + Натисніть двічі посередині, щоб призупинити Використовувати яскравість системи Оновити прогрес перегляду Відновлення даних з резервної копії @@ -230,7 +230,7 @@ Показати постери від Kitsu Автоматичне оновлення плагінів Автоматично встановлювати всі ще не встановлені плагіни з доданих репозиторіїв. - Автоматичний пошук нових оновлень при запуску + Автоматично шукати нові оновлення після запуску застосунку. Оновлення до бета-версій Посилання скопійовано в буфер обміну Деякі телефони не підтримують новий інсталятор пакетів. Спробуйте стару версію, якщо оновлення не встановлюються. @@ -255,7 +255,7 @@ Документальні фільми NSFW Фільм - \@string/ova + OVA Торрент Мітка якості NSFW @@ -283,7 +283,7 @@ Особливості Загальне Випадкова кнопка - Показати випадкову кнопку на Головній сторінці + Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці Мови постачальника Макет програми Бажані медіа @@ -524,10 +524,9 @@ Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com -\nProxy + raw.githubusercontent.com Proxy Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP - Обходити блокування GitHub з використанням jsdlitr, може викликати затримку оновлень на кілька днів. + За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 72d62a04..a14b87cc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -142,14 +142,14 @@ 倍速模式 在播放器中添加播放速度选项 滑动控制进度 - 左右滑动控制播放进度 + 左右滑动以控制视频中的位置 滑动更改设置 上下滑动修改亮度或音量 自动播放下一集 播放完毕后播放下一集 双击控制进度 双击暂停 - 双击控制进度时间 + 双击控制进度时间 (秒) 在左右侧双击快进或快退 双击中间暂停 使用系统亮度 @@ -178,7 +178,7 @@ 自动更新插件 自动下载插件 显示应用更新 - 启动时自动搜索更新 + 启动应用后自动搜索更新。 更新至预览版 搜索预览版更新替代仅搜索完整版本 Github @@ -245,8 +245,8 @@ 电影 电视剧 卡通 - \@string/anime - \@string/ova + 动漫 + OVA 种子 纪录片 亚洲剧 @@ -311,7 +311,7 @@ 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. 通用 随机按钮 - 在主页中显示随机按钮 + 在主页上显示按钮,可以从主页上随机选择电影或电视剧 片源语言 应用布局 首选类型 @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用 jsdelivr 绕过对 Github 的封锁,可能导致更新延迟几天。 + 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 49380b5e..911c0d07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,17 +210,17 @@ Eigengravy Mode Adds a speed option in the player Swipe to seek - Swipe left or right to control time in the videoplayer + Swipe from side to side to control your position in a video Swipe to change settings - Swipe on the left or right side to change brightness or volume + Slide up or down on the left or right side to change brightness or volume Autoplay next episode Start the next episode when the current one ends Double tap to seek Double tap to pause - Player seek amount + Player seek amount (Seconds) Tap twice on the right or left side to seek forwards or backwards - Tap in the middle to pause + Tap twice in the middle to pause Use system brightness Use system brightness in the app player instead of a dark overlay @@ -251,7 +251,7 @@ Automatically download plugins Automatically install all not yet installed plugins from added repositories. Show app updates - Automatically search for new updates on start + Automatically search for new updates after starting the app. Redo setup process Update to prereleases Search for prerelease updates instead of full releases only @@ -324,8 +324,8 @@ Movie Series Cartoon - @string/anime - @string/ova + Anime + OVA Torrent Documentary Asian Drama @@ -383,7 +383,7 @@ Useful for bypassing ISP blocks raw.githubusercontent.com Proxy Failed to reach GitHub, enabling jsdelivr proxy. - Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days. + Using jsdelivr, GitHub blocking can be bypassed. May delay updates by a few days. Clone site Remove site Add a clone of an existing site, with a different URL @@ -428,7 +428,7 @@ Features General Random Button - Show random button on Homepage + Shows button on Homepage which can choose a random movie or TV series from the Homepage Provider languages App Layout Preferred media @@ -657,4 +657,4 @@ Subscribed to %s Unsubscribed from %s Episode %d released! - + \ No newline at end of file From 3a5d8725459a33e9f58a8923e172ef2cae85f0e9 Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 10 Mar 2023 20:01:20 +0000 Subject: [PATCH 256/273] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 1 + 1 file changed, 1 insertion(+) 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 078419e2..4aa859aa 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 @@ -78,6 +78,7 @@ val appLanguages = arrayListOf( Triple("", "ಕನ್ನಡ", "kn"), Triple("", "македонски", "mk"), Triple("", "മലയാളം", "ml"), + Triple("", "bahasa Melayu", "ms"), Triple("", "Nederlands", "nl"), Triple("", "norsk nynorsk", "nn"), Triple("", "norsk bokmål", "no"), From 13ee8e21d06c34a5f01e476ee133a4acc4b854ea Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 10 Mar 2023 21:33:13 +0100 Subject: [PATCH 257/273] Semi-unfucked VLC on A13+ --- .../lagradost/cloudstream3/MainActivity.kt | 31 ++++++++++++------- .../ui/result/ResultViewModel2.kt | 8 ++++- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index a7449255..7818e357 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.os.Build import android.os.Bundle import android.util.AttributeSet import android.util.Log @@ -34,7 +35,6 @@ import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.snackbar.Snackbar import com.jaredrummler.android.colorpicker.ColorPickerDialogListener -import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings @@ -170,7 +170,12 @@ open class ResultResume( val VLC = object : ResultResume( VLC_PACKAGE, - "org.videolan.vlc.player.result", + // 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", ) { @@ -733,15 +738,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } else { this.setKey(getString(R.string.jsdelivr_proxy_key), true) val parentView: View = findViewById(android.R.id.content) - Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG).let { snackbar -> - snackbar.setAction(R.string.revert) { - setKey(getString(R.string.jsdelivr_proxy_key), false) + Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG) + .let { snackbar -> + snackbar.setAction(R.string.revert) { + setKey(getString(R.string.jsdelivr_proxy_key), false) + } + snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) + snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) + snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) + snackbar.show() } - snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground)) - snackbar.setTextColor(colorFromAttribute(R.attr.textColor)) - snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary)) - snackbar.show() - } } } @@ -1123,7 +1129,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { suspend fun checkGithubConnectivity(): Boolean { return try { - app.get("https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", timeout = 5).text.trim() == "ok" + app.get( + "https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", + timeout = 5 + ).text.trim() == "ok" } catch (t: Throwable) { false } 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 2983b41d..46a8c9f6 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 @@ -3,6 +3,7 @@ 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.util.Log import android.widget.Toast @@ -1125,7 +1126,12 @@ class ResultViewModel2 : ViewModel() { 1L } - component = VLC_COMPONENT + // 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) From 29174dbb30a2713844199206cc0b2e5723283f6c Mon Sep 17 00:00:00 2001 From: LikDev-256 <81100289+LikDev-256@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:41:35 +0530 Subject: [PATCH 258/273] Feat: fix Streamsb (#417) * Fix Streamsb * feat(StreamSB) stream break: support audiotracks * Revert "feat(StreamSB) stream break: support audiotracks" This reverts commit 078caf9f88dc92bb7416f51458b1bbea73bfb9bf. * Feat: fix Streamsb They normally update source numbers like 50, 51 but instead of 52 they totally dumped everything and just flipped the number into 15 --- .../main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt index b7477242..cac31328 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/StreamSB.kt @@ -134,7 +134,7 @@ open class StreamSB : ExtractorApi() { it.value.replace(Regex("(embed-|/e/)"), "") }.first() // val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" - val master = "$mainUrl/sources51/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" + val master = "$mainUrl/sources15/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" val headers = mapOf( "watchsb" to "sbstream", ) From 3e2b0f2a17243abbdfddd929544058b7977bc32a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Fri, 10 Mar 2023 20:45:19 +0100 Subject: [PATCH 259/273] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Portuguese) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Dutch) Currently translated at 74.0% (452 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Added translation using Weblate (Malay) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 99.1% (605 of 610 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Russian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Czech) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (English) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Cliff Heraldo <123844876+clxf12@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Felipe Nogueira Co-authored-by: Fjuro Co-authored-by: Frank Gerritsen Mulkes Co-authored-by: Hosted Weblate Co-authored-by: Rex_sa Co-authored-by: Samuel Gadiel Co-authored-by: Skrripy Co-authored-by: TZVS Co-authored-by: Tang Yin Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: gallegonovato Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/en/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-uk/strings.xml | 72 +++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6dca29b4..48856dbb 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -3,7 +3,7 @@ Постер Постер до епізоду Завантаження скасовано - Змінити постачальника + Змінити провайдера Назад Рейтинг: %.1f Актори: %s @@ -22,7 +22,7 @@ Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук на %s… Дані відсутні @@ -64,7 +64,7 @@ Тип контуру Шрифт Розмір шрифту - Пошук за допомогою постачальників + Пошук за допомогою провайдерів Пошук за типами Бананів немає Автовибір мови @@ -75,12 +75,12 @@ Продовжити перегляд Вилучити Детальніше - Цей постачальник є торрентом, рекомендується VPN + Цей провайдер є торрентом, рекомендується VPN Опис Сюжет не знайдено Опис не знайдено Показати Logcat 🐈 - Продовження відтворення в мініатюрному плеєрі поверх інших програм + Продовження відтворення в мініатюрному плеєрі поверх інших застосунків Прибирає чорні рамки Субтитри Субтитри Chromecast @@ -125,7 +125,7 @@ %d Бананів для розробників Кнопка зміни розміру плеєра \@string/home_play - Для коректної роботи цього постачальника може знадобитися VPN + Для коректної роботи цього провайдера може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці Налаштування субтитрів плеєра @@ -147,23 +147,23 @@ Оновлення та резервне копіювання Інформація Розширений пошук - Надає результати пошуку, розділені за постачальниками + Надає результати пошуку, розділені за провайдерами Надсилає дані лише про збої Не надсилає даних - Показати заповнюючий епізод для аніме + Показати філерний епізод для аніме Показати трейлери Приховати вибрану якість відео в результатах пошуку Автоматичне завантаження плагінів - Показати оновлення програми + Показати оновлення застосунку Повторний процес налаштування - Пошук лише попередніх оновлень, а не повних релізів + Пошук лише бета-оновлень, а не повних релізів Встановлювач APK Github Застосунок для легких новел від тих же розробників Застосунок для аніме від тих же розробників Дайте бананів розробникам - Мова програми - Цей постачальник не має підтримки Chromecast + Мова застосунку + Цей провайдер не має підтримки Chromecast Посилань не знайдено Переглянути епізод Скинути до значення за замовчуванням @@ -180,7 +180,7 @@ \nВи впевнені\? %dхв \nзалишилося - Триває + Виходить Завершено Рейтинг Тривалість @@ -189,7 +189,7 @@ За замовчуванням Вільно Зайнято - Програма + Застосунок Телесеріали Мультфільми Аніме @@ -208,7 +208,7 @@ Віддалена помилка Помилка рендеринга Дзеркало Chromecast - Переглянути в програмі + Переглянути в застосунку Переглянути в %s Автозавантаження Завантажити дзеркало @@ -273,7 +273,7 @@ Заповнити Збільшити Доріжки - Оновлення програми + Оновлення застосунку Кеш Жести Особливості плеєра @@ -284,15 +284,15 @@ Загальне Випадкова кнопка Показує кнопку на Головній сторінці, яка може вибрати випадковий фільм або серіал на Головній сторінці - Мови постачальника - Макет програми + Мови провайдера + Макет застосунку Бажані медіа Авто Макет телевізора Макет телефону Макет емулятора Основний колір - Тема програми + Тема застосунку Розташування назви постера Розмістіть назву під постером пароль123 @@ -363,7 +363,7 @@ Кодування субтитрів Включити NSFW на підтримуваних постачальників Макет - Постачальники + Провайдери example.com %s %s Депресивний @@ -429,7 +429,7 @@ Оновлено %d плагіни За замовчуванням в CloudStream не встановлені сайти. Вам потрібно встановити сайти з репозиторіїв. \n -\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунку. +\nЧерез безмозкий DMCA від Sky UK Limited 🤮 ми не можемо прив\'язати сайт репозиторію в застосунок. \n \nПриєднуйтесь до нашого Discord або шукайте в інтернеті. Переглянути репозиторії спільноти @@ -451,28 +451,28 @@ Вбудований плеєр VLC MPV - Відтворення веб-відео - Веб-браузер - Кінець + Відтворення вебвідео + Веббраузер + Ендінґ Коротке повторення Пропустити %s - Змішаний кінець + Змішаний ендінґ Подяки - Опенінг + Опенінґ Вступ Очистити історію Історія - Показувати спливаючі вікна для опенінгу/кінця + Показувати спливаючі вікна для опенінґу/ендінґу Забагато тексту. Не вдалося зберегти в буфер обміну. Позначити як переглянуте Ви впевнені що хочете вийти\? Так Ні - Установлення оновлення програми… - Не вдалося встановити нову версію програми + Встановлення оновлення застосунку… + Не вдалося встановити нову версію застосунку Старий - Інсталятор пакетів - Програму буде оновлено після виходу + Встановлювач пакетів + Застосунок буде оновлено після виходу Це також призведе до видалення всіх плагінів репозиторію Всі мови Назад @@ -484,10 +484,10 @@ Бажаний відеоплеєр Увімкнено безпечний режим Автори - Завантаження оновлення програми… + Завантаження оновлення застосунку… Усі розширення вимкнено через збій, щоб допомогти вам знайти те, що спричиняє проблеми. - Програму не знайдено - Змішаний опенінг + Застосунок не знайдено + Змішаний опенінґ Видалити з переглянутого За оновленням (від старого до нового) За оновленням (від нового до старого) @@ -517,14 +517,14 @@ Журнал Старт Стоп - Тест постачальника + Тест провайдер Оновлення підписаних шоу Підписано Підписано на %s Відписатися від %s Епізод %d випущено! Повернути - raw.githubusercontent.com Proxy + raw.githubusercontent.com Проксі Не вдалося зв\'язатися з GitHub, увімкнувши проксі-сервер jsdelivr. Обходи ISP За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. From 19dc1a2456b658e85bbd2123e85a5cafdcdc651f Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 14 Mar 2023 12:59:32 +0100 Subject: [PATCH 260/273] Un-bruh-momented some translations --- app/src/main/res/values-ar/strings.xml | 2 +- app/src/main/res/values-bg/strings.xml | 6 +++--- app/src/main/res/values-bn/strings.xml | 4 ++-- app/src/main/res/values-bp/strings.xml | 8 ++++---- app/src/main/res/values-cs/strings.xml | 2 +- app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-el/strings.xml | 6 +++--- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fr/strings.xml | 6 +++--- app/src/main/res/values-hr/strings.xml | 6 +++--- app/src/main/res/values-hu/strings.xml | 8 ++++---- app/src/main/res/values-in/strings.xml | 2 +- app/src/main/res/values-it/strings.xml | 2 +- app/src/main/res/values-iw/strings.xml | 6 +++--- app/src/main/res/values-nl/strings.xml | 6 +++--- app/src/main/res/values-no/strings.xml | 8 ++++---- app/src/main/res/values-pl/strings.xml | 6 +++--- app/src/main/res/values-qt/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 2 +- app/src/main/res/values-ru/strings.xml | 2 +- app/src/main/res/values-sk/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 10 +++++----- app/src/main/res/values-tl/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 16 ++++++++-------- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-ur/strings.xml | 2 +- app/src/main/res/values-vi/strings.xml | 4 ++-- app/src/main/res/values-zh-rTW/strings.xml | 16 ++++++++-------- app/src/main/res/values-zh/strings.xml | 12 ++++++------ 29 files changed, 77 insertions(+), 77 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ae45465b..84934288 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -342,7 +342,7 @@ الكل الحد الاقصي الحد الأدنى - \@string/none + @string/none الخطوط المحيطة النمط المنخفض ظل diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index f1f512a1..496512f7 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -105,7 +105,7 @@ Продължете да гледате Премахване Повече информация - \@string/home_play + @string/home_play Може да е необходим VPN, за да работи правилно този доставчик Този доставчик е торент, препоръчва се VPN Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта. @@ -223,8 +223,8 @@ Филм Серия Анимационен филм - \@string/anime - \@string/ova + @string/anime + @string/ova Торент Документален филм Азиатска драма diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 7e0448d6..7c37e291 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -143,8 +143,8 @@ হালনাগাদ ও ব্যাকআপ অ্যাপ এর হালনাগাদ দেখান খুঁজতে সোয়াইপ করুন - \@string/result_poster_img_des - \@string/home_play + @string/result_poster_img_des + @string/home_play আগাতে ডবল ট্যাপ করুন আইজেনগ্রাভি মোড আপডেট শুরু হয়েছে diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml index 2c2e1303..acdf0ae0 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -10,7 +10,7 @@ %dm Poster - \@string/result_poster_img_des + @string/result_poster_img_des Episode Poster Main Poster Next Random @@ -108,7 +108,7 @@ Continue Assistindo Remover Mais Info - \@string/home_play + @string/home_play Uma VPN pode ser necessária para esse fornecedor funcionar corretamente Esse fornecedor é um torrent, uma VPN é recomendada Metadados não são oferecidas pelo site, o carregamento do video pode falhar se ele não existir no site. @@ -222,8 +222,8 @@ Filme Série Desenho Animado - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Documentário Drama Asiático diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 67179b46..1a139511 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -103,7 +103,7 @@ Pokračovat ve sledování Odebrat Další informace - \@string/home_play + @string/home_play Aby tento poskytovatel fungoval správně, budete možná potřebovat VPN Tento poskytovatel je torrent, je doporučená VPN Web neposkytnul žádná metadata, načítání videa selže, pokud na webu neexistuje. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 7cf49de1..e1093e05 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -115,7 +115,7 @@ Weiterschauen Entfernen Mehr Infos - \@string/home_play + @string/home_play Damit dieser Anbieter korrekt funktioniert, ist möglicherweise ein VPN erforderlich Dieser Anbieter bietet Torrents an, ein VPN wird dringend empfohlen Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie auf der Website nicht vorhanden sind. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 5e9dafd8..0d45b2c1 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -387,7 +387,7 @@ Κλείσιμο Εκκαθάριση Γλώσσα υποτίτλων - \@string/home_play + @string/home_play Δεν έχουν παρασχεθεί μεταδεδομένα από τον ιστότοπο, η φόρτωση του βίντεο θα αποτύχει αν δεν υπάρχει στον ιστότοπο. Διπλό πάτημα για παύση Μέγεθος αναζήτησης στο πρόγραμμα αναπαραγωγής @@ -452,7 +452,7 @@ Ανάμεικτοι τίτλοι τέλους -30 Κριτική - \@string/ova + @string/ova Ενημερώσεις εφαρμογής Αντίγραφο ασφαλείας Extensions @@ -464,7 +464,7 @@ Προεπιλεγμένα %s %s Μέγεθος γραμματοσειράς - \@string/anime + @string/anime Σύνδεσμοι Εμφάνιση Χαρακτηριστικά diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 5c8ac532..f036653f 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -194,7 +194,7 @@ Continuar Viendo Remover Más info - \@string/home_play + @string/home_play Una VPN puede ser necesaria para que este proveedor funcione correctamente Este proveedor es un torrent, se recomienda una VPN El sitio no proporciona los metadatos, la carga del video fallará si no existe en el sitio. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index f3d35c19..9fee8c3c 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -240,7 +240,7 @@ Continuer à regarder Retirer Plus d\'informations - \@string/home_play + @string/home_play Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement Ce fournisseur est un torrent, un VPN est recommandé Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site. @@ -385,8 +385,8 @@ 4K Web -30 - \@string/anime - \@string/ova + @string/anime + @string/ova NSFW %s %s Filtrez par langue préférée diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 159542cc..23fd9624 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -119,7 +119,7 @@ Nastavite s gledanjem Makni Više informacija - \@string/home_play + @string/home_play Za ispravan rad ovog pružatelja usluga može biti potreban VPN Ovaj pružatelj usluga je torrent, preporučuje se VPN Stranica ne daje metapodatke, učitavanje videozapisa neće uspjeti ako ne postoji na stranici. @@ -238,8 +238,8 @@ Film Serija Crtić - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Dokumentarac Azijska drama diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5b42fd6a..66526821 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -57,7 +57,7 @@ Megnyitás böngészőben Betöltés kihagyása Poster - \@string/result_poster_img_des + @string/result_poster_img_des Nézés Befejezve Később megnézés @@ -111,7 +111,7 @@ Betűtípusok importálása %s Eltávolítás Több információ - \@string/home_play + @string/home_play VPN szükséges lehet ehhez a szolgáltató megfelelő működéséhez Ez a szolgáltató torrent, VPN ajánlott Leírás @@ -172,11 +172,11 @@ OVA Egyebek Sorozat - \@string/anime + @string/anime Forráshiba NSFW Rajzfilm - \@string/ova + @string/ova Élőadás NSFW Videó diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 1913868a..f5af3877 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -101,7 +101,7 @@ Lanjutkan Menonton Hapus Info lebih lanjut - \@string/home_play + @string/home_play Sebuah VPN mungkin diperlukan agar provider ini bisa bekerja dengan benar Provider ini adalah sebuah torrent, VPN direkomendasikan Metadata tidak disediakan oleh situs, loading video akan gagal jika tidak ada di situs. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b8e7eb20..4476b4a0 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -108,7 +108,7 @@ Continua a guardare Rimuovi Più info - \@string/home_play + @string/home_play Potrebbe essere necessaria una VPN per far funzionare correttamente questo provider Questo provider è un torrent, si raccomanda una VPN I metadati non sono forniti dal sito, il caricamento del video fallirà se non esiste sul sito. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 11cf77ce..4ed5ddc0 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -116,7 +116,7 @@ כתוביות כרומקאסט ממשיך ניגון בנגן מינימלי מעל ישומים אחרים כתוביות - \@string/home_play + @string/home_play היסטוריה מורשת לא @@ -164,8 +164,8 @@ משומש סדרת טלוויזיה סדרות/סרטים מצוירים - \@string/אנימה - \@string/אנימציית וידאו מקורית + @string/אנימה + @string/אנימציית וידאו מקורית דרמה אסייתית כרומקאסט את הפרק כרומקאסט את המראה diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index dd89c34a..3595a24a 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -109,7 +109,7 @@ Doorgaan met kijken Verwijder Meer Info - \@string/home_play + @string/home_play Een VPN kan nodig zijn om deze provider correct te laten werken Deze provider is een torrent, een VPN wordt aanbevolen Metadata wordt niet geleverd door de site, het laden van video\'s zal mislukken als deze niet op de site bestaat. @@ -222,8 +222,8 @@ Film Serie Tekenfilm - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Documentaire Aziatisch drama diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index 41bf704d..d9feb60c 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -2,7 +2,7 @@ Plakat - \@string/result_poster_img_des + @string/result_poster_img_des Episode Plakat Main Plakat Neste tilfeldig @@ -412,7 +412,7 @@ Slå av/på grensesnittselementer på plakat Hopp over denne oppdateringen Forårsaker tilfeldige krasj hvis satt for høyt. Ikke endre dette hvis du ikke har lite minne. - \@string/home_play + @string/home_play Sikkerhetskopier data Data lagret Kunne ikke logge inn på %s @@ -422,11 +422,11 @@ Sensurerbart Vev Lenke til strøm - \@string/anime + @string/anime Skjul valgt videokvalitet i søkeresultater Lastet inn sikkerhetkopifil Oppdateringer og sikkerhetskopi - \@string/ova + @string/ova Avslutt\? Sensurerbart Alle %s er allerede nedlastet diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 558a46ed..7fc0c887 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -455,7 +455,7 @@ Instalator APK Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb legacy, jeśli aktualizacje nie zostaną zainstalowane. password123 - \@string/ova + @string/ova MojaFajnaWitryna MyCoolUsername 127.0.0.1 @@ -463,9 +463,9 @@ przyklad.pl /\?\? Instalator pakietów - \@string/home_play + @string/home_play hello@world.com - \@string/anime + @string/anime Opening Ending Mixed opening diff --git a/app/src/main/res/values-qt/strings.xml b/app/src/main/res/values-qt/strings.xml index 76852ca4..aee3de91 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -247,5 +247,5 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOOOGGAGHAGHAAA aoaaaaaoooghhh oooooh uuaagh - \@string/home_play + @string/home_play \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 42d9b7c8..8cd24a3b 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -106,7 +106,7 @@ Continuați să urmăriți Eliminați Mai multe informații - \@string/home_play + @string/home_play Există probabilitatea necesitații unui VPN pentru ca acest furnizor să funcționeze corespunzător Acest furnizor este un torrent, se recomandă un VPN Metadatele nu sunt furnizate de către site, există posibilitatea ca încărcarea videoclipului să eșueze. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 5295bd35..e9494040 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -363,7 +363,7 @@ Расширения URL репозитория Плагин загружен - \@string/home_play + @string/home_play Перемотка двойным нажатием /\?\? /%d diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 66d8ada9..96fbaff1 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -99,7 +99,7 @@ Tento poskytovateľ je torrent, odporúča sa VPN Importovať písma ich umiestnením do %s Viac informácií - \@string/home_play + @string/home_play Pokračovať v sledovaní Na správne fungovanie tohto poskytovateľa môže byť potrebná VPN Stránka neposkytla žiadne metadáta, načítanie videa zlyhá, ak na stránke neexistuje. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 32336b66..25066d7b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -33,7 +33,7 @@ Undertexter Försök ansluta igen… Gå tillbaka - \@string/result_poster_img_des + @string/result_poster_img_des Spela Avsnitt Ladda ner Intern lagring @@ -44,7 +44,7 @@ Inaktivera automatisk felrapportering Mer information Hide - \@string/result_poster_img_des + @string/result_poster_img_des Spela upp Info Nästa @@ -235,7 +235,7 @@ Episod %d kommer släppas om %d min Visa trailers - \@string/home_play + @string/home_play OVA %d-%d %d %s @@ -244,7 +244,7 @@ %dm \nåterstår NSFW - \@string/ova + @string/ova Torrent NSFW +30 @@ -273,7 +273,7 @@ Asiatiska draman Andra Tecknade serier - \@string/anime + @string/anime Dokumentär Asiatisk drama Video diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 9e5b29d4..721c421c 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -5,7 +5,7 @@ %s Ep %d Poster - \@string/result_poster_img_des + @string/result_poster_img_des Episode Poster Main Poster Next Random diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f53bb69d..975242b2 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -23,9 +23,9 @@ Bölüm Posteri Ana Poster Sonraki Rastgele - \@string/play_episode + @string/play_episode Geri git - \@string/home_change_provider_img_des + @string/home_change_provider_img_des Change Provider Preview Background @@ -46,7 +46,7 @@ Veri yok Daha fazla seçenek Sonraki bölüm - \@string/synopsis + @string/synopsis Türler Paylaş Tarayıcıda aç @@ -123,7 +123,7 @@ İzlemeye devam et Kaldır Daha fazla bilgi - \@string/home_play + @string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir Bu sağlayıcı torrent kullanıyor, bir VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. @@ -205,7 +205,7 @@ Bölüm bulunamadı Dosyayı sil Sil - \@string/sort_cancel + @string/sort_cancel Durdur Sürdür -30 @@ -244,8 +244,8 @@ Film Dizi Çizgi film - \@string/anime - \@string/ova + @string/anime + @string/ova Torrent Belgesel Asya dizisi @@ -368,7 +368,7 @@ Hepsi Maksimum Minimum - \@string/none + @string/none Dış hat Çökmüş Gölge diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 6dca29b4..648de819 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -124,7 +124,7 @@ Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео %d Бананів для розробників Кнопка зміни розміру плеєра - \@string/home_play + @string/home_play Для коректної роботи цього постачальника може знадобитися VPN Метадані не надаються сайтом, завантаження відео не відбудеться, якщо їх немає на сайті. Картинка в картинці diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index f733addc..80081215 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -230,7 +230,7 @@ سلسلہ کارٹون انیمی - \@string/اووا + @string/اووا ٹورینٹ دستاویزی فلم ایشیائی ڈرامے diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 59c65916..74e748a3 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -110,7 +110,7 @@ Tiếp tục xem Loại bỏ Thông tin thêm - \@string/home_play + @string/home_play Bạn có thể sẽ cần sử dụng VPN để xem phim này Phim này được chiếu dưới dạng Torrent. Hãy sử dụng VPN để xem Thông tin phim @@ -229,7 +229,7 @@ Phim Bộ Hoạt Hình Anime - \@string/ova + @string/ova Torrent Phim Tài Liệu Truyền Hình Châu Á diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8a10208a..6aa41ff3 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -23,9 +23,9 @@ 劇集封面 主封面 隨機下一個 - \@string/play_episode + @string/play_episode 返回 - \@string/home_change_provider_img_des + @string/home_change_provider_img_des 更改片源 預覽背景 @@ -46,7 +46,7 @@ 無資料 更多選項 下一集 - \@string/synopsis + @string/synopsis 類型 分享 在瀏覽器中打開 @@ -123,7 +123,7 @@ 繼續觀看 移除 更多資訊 - \@string/home_play + @string/home_play 此片源可能需要 VPN 才能正常使用 此片源是種子,建議使用 VPN 站點不提供元數據,如果站點上不存在元數據,影片載入將失敗。 @@ -205,7 +205,7 @@ 未找到劇集 刪除文件 刪除 - \@string/sort_cancel + @string/sort_cancel 暫停 繼續 -30 @@ -244,8 +244,8 @@ 電影 電視劇 卡通 - \@string/anime - \@string/ova + @string/anime + @string/ova 種子 紀錄片 亞洲劇 @@ -368,7 +368,7 @@ 全部 最大 最小 - \@string/none + @string/none 輪廓 凹陷 陰影 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a14b87cc..574624bc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -23,9 +23,9 @@ 剧集封面 主封面 随机下一个 - \@string/play_episode + @string/play_episode 返回 - \@string/home_change_provider_img_des + @string/home_change_provider_img_des 更改片源 预览背景 @@ -46,7 +46,7 @@ 无数据 更多选项 下一集 - \@string/synopsis + @string/synopsis 类型 分享 在浏览器中打开 @@ -123,7 +123,7 @@ 继续观看 移除 更多信息 - \@string/home_play + @string/home_play 此片源可能需要 VPN 才能正常使用 此片源为种子文件,建议使用 VPN 站点不提供元数据,如果站点上不存在元数据,视频加载将失败。 @@ -206,7 +206,7 @@ 未找到剧集 删除文件 删除 - \@string/sort_cancel + @string/sort_cancel 暂停 继续 -30 @@ -369,7 +369,7 @@ 全部 最大 最小 - \@string/none + @string/none 轮廓 凹陷 阴影 From 2d7126d71f3946d072652c4d6e63c938198bdafe Mon Sep 17 00:00:00 2001 From: Lag <> Date: Tue, 14 Mar 2023 13:12:34 +0100 Subject: [PATCH 261/273] Fix for fix for translations --- app/src/main/res/values-iw/strings.xml | 4 ++-- app/src/main/res/values-ur/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 4ed5ddc0..645724fd 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -164,8 +164,8 @@ משומש סדרת טלוויזיה סדרות/סרטים מצוירים - @string/אנימה - @string/אנימציית וידאו מקורית + @string/anime + @string/ova דרמה אסייתית כרומקאסט את הפרק כרומקאסט את המראה diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 80081215..4a8bbf11 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -230,7 +230,7 @@ سلسلہ کارٹون انیمی - @string/اووا + اووا ٹورینٹ دستاویزی فلم ایشیائی ڈرامے From 7bfcf25df4738741c8553cfce5e96fe711cddea6 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 14 Mar 2023 18:50:13 +0000 Subject: [PATCH 262/273] add a way to autofix weblate's issue with @string --- .github/locales.py | 15 ++++++++++++++- .github/workflows/update_locales.yml | 9 ++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 1c79c093..04d9cd13 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -1,6 +1,7 @@ import re import glob import requests +import lxml.etree as ET # builtin library doesn't preserve comments SETTINGS_PATH = "app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt" @@ -45,4 +46,16 @@ open(SETTINGS_PATH, "w+",encoding='utf-8').write( "\n" + END_MARKER + after_src -) \ No newline at end of file +) + +# Go through each values.xml file and fix escaped \@string +for file in glob.glob(f"{XML_NAME}*/strings.xml"): + try: + tree = ET.parse(file) + for child in tree.getroot(): + if child.text.startswith("\\@string/"): + print(f"[{file}] fixing {child.attrib['name']}") + child.text = child.text.replace("\\@string/", "@string/") + tree.write(file, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=True) + except ET.ParseError as ex: + print(f"[{file}] {ex}") \ No newline at end of file diff --git a/.github/workflows/update_locales.yml b/.github/workflows/update_locales.yml index 93cdca44..628e9bc9 100644 --- a/.github/workflows/update_locales.yml +++ b/.github/workflows/update_locales.yml @@ -1,4 +1,4 @@ -name: Update locale lists +name: Fix locale issues on: workflow_dispatch: @@ -9,7 +9,7 @@ on: - master concurrency: - group: "locale-list" + group: "locale" cancel-in-progress: true jobs: @@ -26,6 +26,9 @@ jobs: - uses: actions/checkout@v2 with: token: ${{ steps.generate_token.outputs.token }} + - name: Install dependencies + run: | + pip3 install lxml - name: Edit files run: | python3 .github/locales.py @@ -35,5 +38,5 @@ jobs: git config --local user.name "recloudstream[bot]" git add . # "echo" returns true so the build succeeds, even if no changed files - git commit -m 'update list of locales' || echo + git commit -m 'chore(locales): fix locale issues' || echo git push From 8ebf5185a3fe95db8adabedf483e34ccda1fbdcb Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 15:46:11 +0100 Subject: [PATCH 263/273] Add ffmpeg audio decoding --- app/build.gradle.kts | 2 + .../cloudstream3/ui/player/CS3IPlayer.kt | 44 +++++++++++-------- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9cbccbe5..f70a575f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -159,6 +159,8 @@ dependencies { implementation("com.google.android.exoplayer:extension-cast:2.18.2") implementation("com.google.android.exoplayer:extension-mediasession:2.18.2") implementation("com.google.android.exoplayer:extension-okhttp:2.18.2") + // Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3 + implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 cb8efe92..2aaa3619 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 @@ -9,8 +9,11 @@ import android.widget.FrameLayout import androidx.preference.PreferenceManager import com.google.android.exoplayer2.* import com.google.android.exoplayer2.C.* +import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON +import com.google.android.exoplayer2.DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER import com.google.android.exoplayer2.database.StandaloneDatabaseProvider import com.google.android.exoplayer2.ext.okhttp.OkHttpDataSource +import com.google.android.exoplayer2.mediacodec.MediaCodecSelector import com.google.android.exoplayer2.source.* import com.google.android.exoplayer2.text.TextRenderer import com.google.android.exoplayer2.trackselection.DefaultTrackSelector @@ -538,7 +541,8 @@ class CS3IPlayer : IPlayer { } // Do no include empty referer, if the provider wants those they can use the header map. - val refererMap = if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) + val refererMap = + if (link.referer.isBlank()) emptyMap() else mapOf("referer" to link.referer) val headers = mapOf( "accept" to "*/*", "sec-ch-ua" to "\"Chromium\";v=\"91\", \" Not;A Brand\";v=\"99\"", @@ -669,23 +673,27 @@ class CS3IPlayer : IPlayer { val exoPlayerBuilder = ExoPlayer.Builder(context) .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> - DefaultRenderersFactory(context).createRenderers( - eventHandler, - videoRendererEventListener, - audioRendererEventListener, - textRendererOutput, - metadataRendererOutput - ).map { - if (it is TextRenderer) { - currentTextRenderer = CustomTextRenderer( - subtitleOffset, - textRendererOutput, - eventHandler.looper, - CustomSubtitleDecoderFactory() - ) - currentTextRenderer!! - } else it - }.toTypedArray() + DefaultRenderersFactory(context).apply { + setEnableDecoderFallback(true) + // Enable Ffmpeg extension + setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) + }.createRenderers( + eventHandler, + videoRendererEventListener, + audioRendererEventListener, + textRendererOutput, + metadataRendererOutput + ).map { + if (it is TextRenderer) { + currentTextRenderer = CustomTextRenderer( + subtitleOffset, + textRendererOutput, + eventHandler.looper, + CustomSubtitleDecoderFactory() + ) + currentTextRenderer!! + } else it + }.toTypedArray() } .setTrackSelector( trackSelector ?: getTrackSelector( From 288c5ffa39d60e0285cef573b20fe6ad4ecf7c29 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 16 Mar 2023 13:00:08 +0100 Subject: [PATCH 264/273] Translated using Weblate (Ukrainian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (610 of 610 strings) Translated using Weblate (German) Currently translated at 100.0% (610 of 610 strings) Co-authored-by: Anarchydr Co-authored-by: Hosted Weblate Co-authored-by: Julian Co-authored-by: Sdarfeesh Co-authored-by: Skrripy Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-de/strings.xml | 16 ++++++++-------- app/src/main/res/values-hr/strings.xml | 18 +++++++++--------- app/src/main/res/values-uk/strings.xml | 2 +- app/src/main/res/values-zh/strings.xml | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e1093e05..911705d5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -41,7 +41,7 @@ Suche %s… Keine Daten vorhanden Mehr Optionen - Nächste Epsisode + Nächste Episode Genres Teilen In Browser öffnen @@ -136,14 +136,14 @@ Wischen zum vor- und zurückspulen Nach links oder rechts wischen, um die Zeit im Videoplayer zu steuern Wischen, um Einstellungen zu ändern - Links oder rechts wischen, um die Helligkeit oder Lautstärke zu ändern + Links oder rechts nach oben oder unten wischen, um die Helligkeit oder Lautstärke zu ändern Nächste Episode automatisch abspielen Nächste Episode wird gestartet, sobald die aktuelle Episode endet Doppeltippen zum vor- und zurückspulen Doppeltippen zum Pausieren - Zeit für vor- und zurückspulen im Player + Zeit für vor- und zurückspulen im Player (Sekunden) Zweimal auf die rechte oder linke Seite tippen, um vor- oder zurückzuspulen - In die Mitte tippen, um zu pausieren + Doppelt in die Mitte tippen, um zu pausieren Systemhelligkeit verwenden Systemhelligkeit anstelle eines dunklen Overlay im Player verwenden Episodenfortschritt aktualisieren @@ -166,7 +166,7 @@ Ausgewählte Videoqualität bei Suchergebnissen ausblenden Automatische Plugin-Updates App-Updates anzeigen - Automatisches Suchen nach neuen Updates beim Start + Automatisches Suchen nach neuen Updates nach dem Start Auf Vorabversionen updaten Suche nach Vorabversionen statt nur nach Vollversionen Github @@ -286,7 +286,7 @@ Haftungsausschluss Allgemein Zufalls-Button - Zufallsbutton auf der Startseite anzeigen + Zeigt einen Zufallsbutton auf der Startseite an, mit welchem eine Serie oder ein Film von der Website zufällig ausgewählt wird Anbieter-Sprachen App-Layout Bevorzugte Medien @@ -519,13 +519,13 @@ Start Neustarten Bevorzugte Videoqualität (mobile Daten) - Umgehung der GitHub Sperre mit jsdelivr, kann zu einigen Tagen Verzögerung bei Updates führen. + Umgehung der GitHub Sperre mit jsdelivr. Kann zu einigen Tagen Verzögerung bei Updates führen. %s abonniert %s deabonniert Episode %d erschienen! raw.githubusercontent.com Proxy GitHub kann nicht erreicht werden, der jsdelivr-Proxy wird aktiviert. - Aktualisierung abonnierter Sendungen + Abonnierte Serien werden aktualisiert Rückgängig Abonniert ISP-Umgehungen diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 23fd9624..5366fe34 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -138,16 +138,16 @@ Eigengravy način Dodaje opciju brzine u playeru Prijeđi prstom za traženje - Prijeđi prstom ulijevo ili udesno za kontrolu vremena u videoplayeru + Prijeđite prstom ulijevo ili udesno kako biste kontrolirali player Klizni za promjenu postavki - Prijeđi prstom ulijevo ili udesno za promjenu svjetline ili glasnoće + Kliznite prstom ulijevo ili udesno za promjenu svjetline ili glasnoće Automatski započni sljedeću epizodu Započne sljedeću epizodu kad trenutna završi Dodirni dvaput za traženje Dodirni dvaput za pauziranje - Iznos preskakanja u playeru + Iznos preskakanja u playeru (Sekunde) Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag - Dodirni u sredinu zaslona za pauziranje + Dodirnite dvaput u sredinu zaslona za pauziranje Koristi svijetlinu u sustavu Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog preklopa Ažuriraj napredak gledanja @@ -173,7 +173,7 @@ Sakrij odabranu kvalitetu videozapisa u rezultatima pretraživanja Automatsko ažuriranje dodataka Prikaži ažuriranja aplikacije - Automatski traži nova ažuriranja pri pokretanju aplikacije + Automatski traži nova ažuriranja nakon pokretanja aplikacije Ažuriranje na predizdanja Tražite ažuriranja prije izdanja umjesto samo potpunih izdanja Github @@ -238,8 +238,8 @@ Film Serija Crtić - @string/anime - @string/ova + Anime + OVA Torrent Dokumentarac Azijska drama @@ -299,7 +299,7 @@ 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 Random gumb - Prikaži random gumb na početnoj stranici + Prikazuje gumb na početnoj stranici koji može odabrati nasumični film ili TV seriju s početne stranice Jezici pružatelja usluga Izgled aplikacije Preferirani mediji @@ -552,6 +552,6 @@ ISP zaobilaznice raw.githubusercontent.com Proxy Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. - Zaobilazi blokiranje GitHuba pomoću jsdelivr, može uzrokovati odgode ažuriranja za nekoliko dana. + Koristeći jsdelivr, GitHub blokiranje se može zaobići. Može odgoditi ažuriranja za nekoliko dana. Preferirana kvaliteta gledanja (podatkovna mobilna mreža) \ 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 648de819..d9ec76bb 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -22,7 +22,7 @@ Пошук Завантаження %d хв - Параметри + Налаштування Пошук… Пошук на %s… Дані відсутні diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 574624bc..47807259 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -573,7 +573,7 @@ 日志 raw.githubusercontent.com 代理 连接 Github 失败,正在启用 jsdelivr 代理。 - 使用jsdelivr,可以绕过GitHub的封锁。可能会延迟几天的更新。 + 使用 jsdelivr,可以绕过 GitHub 的封锁。可能会延迟几天的更新。 ISP 绕过 还原 首选播放画质(移动数据) From 67318a62a37673f1acef39dc60684a0b9e005def Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:04:00 +0000 Subject: [PATCH 265/273] chore(locales): fix locale issues --- app/src/main/res/values-ar/strings.xml | 4 ++-- app/src/main/res/values-bg/strings.xml | 7 ++++--- app/src/main/res/values-bn/strings.xml | 4 ++-- app/src/main/res/values-bp/strings.xml | 7 ++++--- app/src/main/res/values-cs/strings.xml | 7 ++++--- app/src/main/res/values-de/strings.xml | 4 ++-- app/src/main/res/values-el/strings.xml | 7 ++++--- app/src/main/res/values-eo/strings.xml | 4 ++-- app/src/main/res/values-es/strings.xml | 4 ++-- app/src/main/res/values-fa/strings.xml | 4 ++-- app/src/main/res/values-fr/strings.xml | 7 ++++--- app/src/main/res/values-hi/strings.xml | 7 ++++--- app/src/main/res/values-hr/strings.xml | 7 ++++--- app/src/main/res/values-hu/strings.xml | 4 ++-- app/src/main/res/values-in/strings.xml | 7 ++++--- app/src/main/res/values-it/strings.xml | 7 ++++--- app/src/main/res/values-iw/strings.xml | 4 ++-- app/src/main/res/values-ja/strings.xml | 4 ++-- app/src/main/res/values-kn/strings.xml | 4 ++-- app/src/main/res/values-mk/strings.xml | 7 ++++--- app/src/main/res/values-ml/strings.xml | 7 ++++--- app/src/main/res/values-ms/strings.xml | 4 ++-- app/src/main/res/values-nl/strings.xml | 7 ++++--- app/src/main/res/values-nn/strings.xml | 4 ++-- app/src/main/res/values-no/strings.xml | 4 ++-- app/src/main/res/values-pl/strings.xml | 7 ++++--- app/src/main/res/values-pt/strings.xml | 4 ++-- app/src/main/res/values-qt/strings.xml | 4 ++-- app/src/main/res/values-ro/strings.xml | 7 ++++--- app/src/main/res/values-ru/strings.xml | 4 ++-- app/src/main/res/values-sk/strings.xml | 4 ++-- app/src/main/res/values-so/strings.xml | 4 ++-- app/src/main/res/values-sv/strings.xml | 7 ++++--- app/src/main/res/values-ta/strings.xml | 4 ++-- app/src/main/res/values-tl/strings.xml | 7 ++++--- app/src/main/res/values-tr/strings.xml | 7 ++++--- app/src/main/res/values-uk/strings.xml | 4 ++-- app/src/main/res/values-ur/strings.xml | 4 ++-- app/src/main/res/values-vi/strings.xml | 7 ++++--- app/src/main/res/values-zh-rTW/strings.xml | 7 ++++--- app/src/main/res/values-zh/strings.xml | 7 ++++--- 41 files changed, 122 insertions(+), 102 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 84934288..2a356812 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -1,4 +1,4 @@ - + ملصق @@ -561,4 +561,4 @@ باستخدام jsdelivr ، يمكن تجاوز حظر GitHub. قد يؤخر التحديثات لبضعة أيام. وكيل raw.githubusercontent.com جودة المشاهدة المفضلة (بيانات الجوال) - \ No newline at end of file + diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 496512f7..301242cd 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -1,5 +1,6 @@ - - + + + %s еп. %d Актьори: %s @@ -497,4 +498,4 @@ Приложението ще се актуализира при изход от него Започна Актуализация Премахване от гледани - \ No newline at end of file + diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 7c37e291..71d5d6d0 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -1,4 +1,4 @@ - + পোস্টার ক্লাউডস্ট্রিম দিয়ে চালান @@ -148,4 +148,4 @@ আগাতে ডবল ট্যাপ করুন আইজেনগ্রাভি মোড আপডেট শুরু হয়েছে - \ 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 acdf0ae0..13b34872 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -428,4 +429,4 @@ Começa o próximo episódio quando o atual termina Ativar NSFW em fornecedores compatíveis Fornecedores - \ 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 1a139511..1501a5d9 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -553,4 +554,4 @@ Vrátit zpět Pomocí jsdelivr lze obejít blokování GitHubu. Může dojít ke zpoždění aktualizací o několik dní. Obcházení ISP - \ 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 911705d5..8fbcc2d0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -1,4 +1,4 @@ - + %s Ep %d Besetzung: %s @@ -529,4 +529,4 @@ Rückgängig Abonniert ISP-Umgehungen - \ No newline at end of file + diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0d45b2c1..f07ce43c 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -1,5 +1,6 @@ - - + + + CloudStream Αρχική Αναζήτηση @@ -508,4 +509,4 @@ \nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! \nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο. - \ No newline at end of file + diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index 09e6941d..5eac8686 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -1,4 +1,4 @@ - + Reen Hejmo @@ -78,4 +78,4 @@ Rapido (%.2fx) Serĉi… Elŝuti - \ 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 f036653f..06c20aa5 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,4 +1,4 @@ - + Extensiones Descargue la lista de sitios que quiera utilizar @@ -529,4 +529,4 @@ Revertir ISP Bypasses Calidad de visualización preferida (Datos móviles) - \ No newline at end of file + diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 81853674..e4c23628 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1,4 +1,4 @@ - + حذف مکث @@ -33,4 +33,4 @@ %dساعت %dدقیقه %dدقیقه پوستر اصلی - \ 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 9fee8c3c..b96ff0cd 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -1,5 +1,6 @@ - - + + + CloudStream Accueil Rechercher @@ -523,4 +524,4 @@ Contournements de FAI L\'épisode %d est sorti ! Échouer - \ No newline at end of file + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index f33a2336..833b76f4 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -1,5 +1,6 @@ - - + + + रफ्तार (%.2fx) नया अपडेट आया है! @@ -146,4 +147,4 @@ %dh %dm %dm विज्ञापन - \ No newline at end of file + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 5366fe34..b4931377 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -554,4 +555,4 @@ Neuspješno dohvaćanje GitHuba, omogućavanje jsdelivr proxyja. Koristeći jsdelivr, GitHub blokiranje se može zaobići. Može odgoditi ažuriranja za nekoliko dana. Preferirana kvaliteta gledanja (podatkovna mobilna mreža) - \ 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 66526821..1389dff0 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -1,4 +1,4 @@ - + Stáblista: %s %dn %dó%dp @@ -275,4 +275,4 @@ Minőségi jelzés Szinkroncímke Alcímke - \ 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 f5af3877..02234c49 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Pemeran: %s @@ -552,4 +553,4 @@ Bypass ISP Pulihkan Nonton dengan kualitas yang di inginkan (Data Seluler) - \ No newline at end of file + diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 4476b4a0..eca60da1 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Cast: %s @@ -551,4 +552,4 @@ Aggiornando shows a cui sei iscritto L\'episodio %d è stato rilasciato! Qualità di visualizzazione preferita (Dati mobili) - \ 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 645724fd..b24f0c60 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -1,4 +1,4 @@ - + הרקע של ההצגה לפני צוות שחקנים: %s @@ -506,4 +506,4 @@ אלפביתי (ת\' עד א\') פתח עם נראה שהרשימה הזו ריקה, נסו לעבור לרשימה אחרת - \ No newline at end of file + diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index a3d1d434..20641b20 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -1,4 +1,4 @@ - + %d分 ダウンロード @@ -182,4 +182,4 @@ アップデートを確認 作品名 アプリのアップデートをインストール中… - \ No newline at end of file + diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index 242653be..4b7b6869 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,4 +1,4 @@ - + %sಎಪಿ%d ಕ್ಯಾಸ್ಟ್:%s @@ -125,4 +125,4 @@ ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ ಮುಂದಿನ ರಾಂಡಮ್ - \ No newline at end of file + diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 7251d0d7..811a09c5 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -1,5 +1,6 @@ - - + + + Брзина (%.2fx) Оценето: %.1f @@ -213,4 +214,4 @@ Сенка Подигнат Историја - \ No newline at end of file + diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index b6ad3a80..d430d7cc 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -1,5 +1,6 @@ - - + + + വേഗം (%.2fx) റേറ്റിംഗ്: %.1f @@ -169,4 +170,4 @@ ഔചിത്യ വീഡിയോ ക്വാളിറ്റി ചരിത്രം കണ്ടതാണെന്ന് അടയാളപ്പെടുത്തുക - \ No newline at end of file + diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index a6b3daec..c757504a 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -1,2 +1,2 @@ - - \ No newline at end of file + + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 3595a24a..766bcdc7 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Cast: %s @@ -408,4 +409,4 @@ Bibliotheek Browser Logboek - \ No newline at end of file + diff --git a/app/src/main/res/values-nn/strings.xml b/app/src/main/res/values-nn/strings.xml index b5132028..43738665 100644 --- a/app/src/main/res/values-nn/strings.xml +++ b/app/src/main/res/values-nn/strings.xml @@ -1,4 +1,4 @@ - + Fleire val Heim @@ -183,4 +183,4 @@ Varigheit Direktesendingar Programoppdateringar - \ No newline at end of file + diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index d9feb60c..fddd4919 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -1,4 +1,4 @@ - + Plakat @@ -492,4 +492,4 @@ Oppdatering startet Programtillegg nedlastet Programmet vil oppgraderes når du avslutter det - \ 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 7fc0c887..a2a07dd7 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -1,5 +1,6 @@ - - + + + Prędkość (%.2fx) Ocena: %.1f Znaleziono nową aktualizację! @@ -532,4 +533,4 @@ Obchodzi blokadę GitHuba za pomocą jsdelivr, może spowodować opóźnienie aktualizacji o kilka dni. Nie udało się połączyć z GitHub, włączono serwer pośredniczący jsdelivr. Domyślna jakość (dane mobilne) - \ 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 64ccb903..dd722f62 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -1,4 +1,4 @@ - + %s Ep %d %dh %dm @@ -529,4 +529,4 @@ Configurações padrão SD Faixas de áudio - \ 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 aee3de91..eee28785 100644 --- a/app/src/main/res/values-qt/strings.xml +++ b/app/src/main/res/values-qt/strings.xml @@ -1,4 +1,4 @@ - + aauugghhaauuh @@ -248,4 +248,4 @@ aoaaaaaoooghhh oooooh uuaagh @string/home_play - \ No newline at end of file + diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 8cd24a3b..aa443783 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d Distribuție: %s @@ -388,4 +389,4 @@ Log Browser Joacă cu CloudStream - \ 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 e9494040..9d8f6895 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,4 +1,4 @@ - + История Нет @@ -529,4 +529,4 @@ Обход ограничения доступа к GitHub с помощью jsdelivr может задержать обновления на несколько дней. Подписные Отказались от подписки на %s - \ No newline at end of file + diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 96fbaff1..a1afd6d9 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -1,4 +1,4 @@ - + Našla sa nová aktualizácia! \n%s -> %s @@ -104,4 +104,4 @@ Na správne fungovanie tohto poskytovateľa môže byť potrebná VPN Stránka neposkytla žiadne metadáta, načítanie videa zlyhá, ak na stránke neexistuje. Popis - \ No newline at end of file + diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index b944b6b3..ce7d557a 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -1,4 +1,4 @@ - + Metalaya: %s %dm %ds %dd @@ -487,4 +487,4 @@ Bilowga Bilow isku qasan Qoraalka dhamaadka - \ 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 25066d7b..0b7ba89e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -1,5 +1,6 @@ - - + + + Betygsatt: %.1f Hastighet (%.2fx) Ny uppdatering hittad! @@ -368,4 +369,4 @@ Titta på videor på dessa språk Föregående Spår - \ No newline at end of file + diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml index b2334c5f..4370e760 100644 --- a/app/src/main/res/values-ta/strings.xml +++ b/app/src/main/res/values-ta/strings.xml @@ -1,4 +1,4 @@ - + தேடுக தேடல் %s… @@ -107,4 +107,4 @@ இடைநிறுத்துவதற்கு இருமுறை தட்டவும் Chromecast வசன அமைப்புகள் இருண்ட மேலடுக்குக்குப் பதிலாக ஆப் பிளேயரில் சிஸ்டம் பிரகாசத்தைப் பயன்படுத்தவும் - \ No newline at end of file + diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 721c421c..cf3b1263 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Ep %d @@ -263,4 +264,4 @@ Magdagdag ng Account Kasaysayan I-tanda bilang napanood na - \ No newline at end of file + diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 975242b2..74754008 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -577,4 +578,4 @@ %s kanalı aboneliğinden çıkıldı Günlük Oynatıcı görünür durumdayken atlanacak süre miktarı - \ 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 d9ec76bb..bd062394 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -1,4 +1,4 @@ - + Постер Постер до епізоду @@ -529,4 +529,4 @@ Обходи ISP За допомогою jsdelivr можна обійти блокування GitHub. Можлива затримка оновлень на кілька днів. Бажана якість перегляду (Мобільні дані) - \ 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 4a8bbf11..c19c6472 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -1,4 +1,4 @@ - + کاسٹ: %s قسط %d جاری کیا جائے گا @@ -356,4 +356,4 @@ %d / 10 اٹھایا اگر سب ٹائٹلز %d ms بہت جلد دکھائے جائیں تو اسے استعمال کریں - \ 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 74e748a3..520cfaa4 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1,5 +1,6 @@ - - + + + %s Tập %d @@ -524,4 +525,4 @@ Thất bại Thành công Bắt đầu - \ 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 6aa41ff3..3364ea86 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -534,4 +535,4 @@ 外觀 功能 瀏覽器 - \ No newline at end of file + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 47807259..44b93430 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,5 +1,6 @@ - - + + + %d %s | %s %s • %s @@ -577,4 +578,4 @@ ISP 绕过 还原 首选播放画质(移动数据) - \ No newline at end of file + From 8fff809b792dc8f9885f71509bdde11427d9e378 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 16:07:28 +0100 Subject: [PATCH 266/273] Revert ffmpeg as it causes issues with subtitles :( --- app/build.gradle.kts | 2 +- .../java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f70a575f..0bd56fe7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -160,7 +160,7 @@ dependencies { implementation("com.google.android.exoplayer:extension-mediasession:2.18.2") implementation("com.google.android.exoplayer:extension-okhttp:2.18.2") // Use the Jellyfin ffmpeg extension for easy ffmpeg audio decoding in exoplayer. Thank you Jellyfin <3 - implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") +// implementation("org.jellyfin.exoplayer:exoplayer-ffmpeg-extension:2.18.2+1") //implementation("com.google.android.exoplayer:extension-leanback:2.14.0") 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 2aaa3619..e0885671 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 @@ -674,9 +674,9 @@ class CS3IPlayer : IPlayer { ExoPlayer.Builder(context) .setRenderersFactory { eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput -> DefaultRenderersFactory(context).apply { - setEnableDecoderFallback(true) +// setEnableDecoderFallback(true) // Enable Ffmpeg extension - setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) +// setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON) }.createRenderers( eventHandler, videoRendererEventListener, From 019399952f4516a1478875c0ca1c3918e55f0788 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 16:23:03 +0100 Subject: [PATCH 267/273] Better subtitle decoding :) --- .../ui/player/CustomSubtitleDecoderFactory.kt | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt index 690d3706..974a5d26 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CustomSubtitleDecoderFactory.kt @@ -4,13 +4,16 @@ import android.content.Context import android.util.Log import androidx.preference.PreferenceManager import com.google.android.exoplayer2.Format -import com.google.android.exoplayer2.text.SubtitleDecoder -import com.google.android.exoplayer2.text.SubtitleDecoderFactory -import com.google.android.exoplayer2.text.SubtitleInputBuffer -import com.google.android.exoplayer2.text.SubtitleOutputBuffer +import com.google.android.exoplayer2.text.* +import com.google.android.exoplayer2.text.cea.Cea608Decoder +import com.google.android.exoplayer2.text.cea.Cea708Decoder +import com.google.android.exoplayer2.text.dvb.DvbDecoder +import com.google.android.exoplayer2.text.pgs.PgsDecoder import com.google.android.exoplayer2.text.ssa.SsaDecoder import com.google.android.exoplayer2.text.subrip.SubripDecoder import com.google.android.exoplayer2.text.ttml.TtmlDecoder +import com.google.android.exoplayer2.text.tx3g.Tx3gDecoder +import com.google.android.exoplayer2.text.webvtt.Mp4WebvttDecoder import com.google.android.exoplayer2.text.webvtt.WebvttDecoder import com.google.android.exoplayer2.util.MimeTypes import com.lagradost.cloudstream3.R @@ -19,7 +22,11 @@ import org.mozilla.universalchardet.UniversalDetector import java.nio.ByteBuffer import java.nio.charset.Charset -class CustomDecoder : SubtitleDecoder { +/** + * @param fallbackFormat used to create a decoder based on mimetype if the subtitle string is not + * enough to identify the subtitle format. + **/ +class CustomDecoder(private val fallbackFormat: Format?) : SubtitleDecoder { companion object { fun updateForcedEncoding(context: Context) { val settingsManager = PreferenceManager.getDefaultSharedPreferences(context) @@ -139,7 +146,7 @@ class CustomDecoder : SubtitleDecoder { val inputString = getStr(inputBuffer) if (realDecoder == null && !inputString.isNullOrBlank()) { var str: String = inputString - // this way we read the subtitle file and decide what decoder to use instead of relying on mimetype + // this way we read the subtitle file and decide what decoder to use instead of relying fully on mimetype Log.i(TAG, "Got data from queueInputBuffer") //https://github.com/LagradOst/CloudStream-2/blob/ddd774ee66810137ff7bd65dae70bcf3ba2d2489/CloudStreamForms/CloudStreamForms/Script/MainChrome.cs#L388 realDecoder = when { @@ -148,8 +155,31 @@ class CustomDecoder : SubtitleDecoder { (str.startsWith( "[Script Info]", ignoreCase = true - ) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder() + ) || str.startsWith("Title:", ignoreCase = true)) -> SsaDecoder(fallbackFormat?.initializationData) str.startsWith("1", ignoreCase = true) -> SubripDecoder() + fallbackFormat != null -> { + when (val mimeType = fallbackFormat.sampleMimeType) { + MimeTypes.TEXT_VTT -> WebvttDecoder() + MimeTypes.TEXT_SSA -> SsaDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() + MimeTypes.APPLICATION_TTML -> TtmlDecoder() + MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() + MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> Cea608Decoder( + mimeType, + fallbackFormat.accessibilityChannel, + Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS + ) + MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( + fallbackFormat.accessibilityChannel, + fallbackFormat.initializationData + ) + MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(fallbackFormat.initializationData) + MimeTypes.APPLICATION_PGS -> PgsDecoder() + MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() + else -> null + } + } else -> null } Log.i( @@ -246,28 +276,6 @@ class CustomSubtitleDecoderFactory : SubtitleDecoderFactory { } override fun createDecoder(format: Format): SubtitleDecoder { - return CustomDecoder() - //return when (val mimeType = format.sampleMimeType) { - // MimeTypes.TEXT_VTT -> WebvttDecoder() - // MimeTypes.TEXT_SSA -> SsaDecoder(format.initializationData) - // MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttDecoder() - // MimeTypes.APPLICATION_TTML -> TtmlDecoder() - // MimeTypes.APPLICATION_SUBRIP -> SubripDecoder() - // MimeTypes.APPLICATION_TX3G -> Tx3gDecoder(format.initializationData) - // MimeTypes.APPLICATION_CEA608, MimeTypes.APPLICATION_MP4CEA608 -> return Cea608Decoder( - // mimeType, - // format.accessibilityChannel, - // Cea608Decoder.MIN_DATA_CHANNEL_TIMEOUT_MS - // ) - // MimeTypes.APPLICATION_CEA708 -> Cea708Decoder( - // format.accessibilityChannel, - // format.initializationData - // ) - // MimeTypes.APPLICATION_DVBSUBS -> DvbDecoder(format.initializationData) - // MimeTypes.APPLICATION_PGS -> PgsDecoder() - // MimeTypes.TEXT_EXOPLAYER_CUES -> ExoplayerCuesDecoder() - // // Default WebVttDecoder - // else -> WebvttDecoder() - //} + return CustomDecoder(format) } } \ No newline at end of file From 9c40abc4d32f2003d84361828435683b031dc0e0 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Fri, 17 Mar 2023 22:15:25 +0100 Subject: [PATCH 268/273] Added player intent --- app/src/main/AndroidManifest.xml | 10 +++++++++ .../lagradost/cloudstream3/MainActivity.kt | 21 +++++++++++++++++++ .../syncproviders/AccountManager.kt | 1 + .../ui/download/DownloadFragment.kt | 4 ++-- .../ui/player/DownloadedPlayerActivity.kt | 2 +- .../cloudstream3/ui/player/LinkGenerator.kt | 17 ++++++++++----- 6 files changed, 47 insertions(+), 8 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 871c4f69..563c82f8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,6 +98,16 @@ + + + + + + + + + + diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 7818e357..d054f504 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration +import android.net.Uri import android.os.Build import android.os.Bundle import android.util.AttributeSet @@ -57,6 +58,7 @@ import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.OAuth2Apis import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.accountManagers import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appString +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringPlayer import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringResumeWatching import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch @@ -65,6 +67,9 @@ 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.home.HomeViewModel +import com.lagradost.cloudstream3.ui.player.BasicLink +import com.lagradost.cloudstream3.ui.player.GeneratorPlayer +import com.lagradost.cloudstream3.ui.player.LinkGenerator import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST import com.lagradost.cloudstream3.ui.result.setImage @@ -274,6 +279,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { isWebview: Boolean ): Boolean = with(activity) { + // TODO MUCH BETTER HANDLING + // Invalid URIs can crash fun safeURI(uri: String) = normalSafeApiCall { URI(uri) } @@ -329,6 +336,20 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { // It might be better to use the QuickSearch. nav_view?.selectedItemId = R.id.navigation_search nav_rail_view?.selectedItemId = R.id.navigation_search + } else if (safeURI(str)?.scheme == appStringPlayer) { + val uri = Uri.parse(str) + val name = uri.getQueryParameter("name") + val url = URLDecoder.decode(uri.authority, "UTF-8") + + navigate( + R.id.global_to_navigation_player, + GeneratorPlayer.newInstance( + LinkGenerator( + listOf(BasicLink(url, name)), + extract = true, + ) + ) + ) } else if (safeURI(str)?.scheme == appStringResumeWatching) { val id = str.substringAfter("$appStringResumeWatching://").toIntOrNull() diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index f17086c1..8ce6bae2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -45,6 +45,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { const val appString = "cloudstreamapp" const val appStringRepo = "cloudstreamrepo" + const val appStringPlayer = "cloudstreamplayer" // Instantly start the search given a query const val appStringSearch = "cloudstreamsearch" 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 f0340845..e80a8fa5 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 @@ -24,7 +24,6 @@ import com.lagradost.cloudstream3.mvvm.observe import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.player.GeneratorPlayer import com.lagradost.cloudstream3.ui.player.LinkGenerator -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE @@ -40,6 +39,7 @@ import kotlinx.android.synthetic.main.stream_input.* import android.text.format.Formatter.formatShortFileSize import androidx.core.widget.doOnTextChanged import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.ui.player.BasicLink import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import java.net.URI @@ -225,7 +225,7 @@ class DownloadFragment : Fragment() { R.id.global_to_navigation_player, GeneratorPlayer.newInstance( LinkGenerator( - listOf(url), + listOf(BasicLink(url)), extract = true, referer = referer, isM3u8 = dialog.hls_switch?.isChecked 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 dc1bbba3..6f40e145 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 @@ -42,7 +42,7 @@ class DownloadedPlayerActivity : AppCompatActivity() { R.id.global_to_navigation_player, GeneratorPlayer.newInstance( LinkGenerator( listOf( - url + BasicLink(url) ) ) ) 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 1f242481..0b560857 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 @@ -5,8 +5,15 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.cloudstream3.utils.* import java.net.URI +/** + * Used to open the player more easily with the LinkGenerator + **/ +data class BasicLink( + val url: String, + val name: String? = null, +) class LinkGenerator( - private val links: List, + private val links: List, private val extract: Boolean = true, private val referer: String? = null, private val isM3u8: Boolean? = null @@ -47,7 +54,7 @@ class LinkGenerator( offset: Int ): Boolean { links.amap { link -> - if (!extract || !loadExtractor(link, referer, { + if (!extract || !loadExtractor(link.url, referer, { subtitleCallback(PlayerSubtitleHelper.getSubtitleData(it)) }) { callback(it to null) @@ -57,11 +64,11 @@ class LinkGenerator( callback( ExtractorLink( "", - link, - unshortenLinkSafe(link), // unshorten because it might be a raw link + link.name ?: link.url, + unshortenLinkSafe(link.url), // unshorten because it might be a raw link referer ?: "", Qualities.Unknown.value, isM3u8 ?: normalSafeApiCall { - URI(link).path?.substringAfterLast(".")?.contains("m3u") + URI(link.url).path?.substringAfterLast(".")?.contains("m3u") } ?: false ) to null ) From 5245eff6e12a781bb2e072e75d7e610252c4135d Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sat, 18 Mar 2023 09:22:07 +0100 Subject: [PATCH 269/273] [skip ci] fix xml header being slightly wrong --- .github/locales.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 04d9cd13..9ab272b9 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -56,6 +56,8 @@ for file in glob.glob(f"{XML_NAME}*/strings.xml"): if child.text.startswith("\\@string/"): print(f"[{file}] fixing {child.attrib['name']}") child.text = child.text.replace("\\@string/", "@string/") - tree.write(file, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=True) + with open(file, 'w') as fp: + fp.write('\n') + tree.write(fp, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=False) except ET.ParseError as ex: - print(f"[{file}] {ex}") \ No newline at end of file + print(f"[{file}] {ex}") From 4235c826a5f150c69af0f601e76855bbf12e9971 Mon Sep 17 00:00:00 2001 From: Lag <> Date: Sat, 18 Mar 2023 23:55:58 +0100 Subject: [PATCH 270/273] Better focus on Android TV (Thank you ocean for reporting) --- .../ui/home/HomeParentItemAdapter.kt | 2 +- .../ui/result/LinearListLayout.kt | 18 +++++++++---- .../cloudstream3/ui/result/ResultFragment.kt | 20 ++++++++++++++ .../ui/settings/SettingsAccount.kt | 22 +++++++++++++++ .../main/res/layout/fragment_result_tv.xml | 9 +++---- .../main/res/layout/homepage_parent_tv.xml | 27 +++++++++---------- 6 files changed, 71 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt index e6999c9e..58c6dbe0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt @@ -185,7 +185,7 @@ open class ParentItemAdapter( ) : RecyclerView.ViewHolder(itemView) { val title: TextView = itemView.home_child_more_info - val recyclerView: RecyclerView = itemView.home_child_recyclerview + private val recyclerView: RecyclerView = itemView.home_child_recyclerview fun update(expand: HomeViewModel.ExpandableHomepageList) { val info = expand.list diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt index 59a46264..affbcbb4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/LinearListLayout.kt @@ -7,13 +7,13 @@ import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.mvvm.logError fun RecyclerView?.setLinearListLayout(isHorizontal: Boolean = true) { - if(this == null) return + if (this == null) return this.layoutManager = this.context?.let { LinearListLayout(it).apply { if (isHorizontal) setHorizontal() else setVertical() } } ?: this.layoutManager } -class LinearListLayout(context: Context?) : +open class LinearListLayout(context: Context?) : LinearLayoutManager(context) { fun setHorizontal() { @@ -24,7 +24,8 @@ class LinearListLayout(context: Context?) : orientation = VERTICAL } - private fun getCorrectParent(focused: View): View? { + private fun getCorrectParent(focused: View?): View? { + if (focused == null) return null var current: View? = focused val last: ArrayList = arrayListOf(focused) while (current != null && current !is RecyclerView) { @@ -54,10 +55,17 @@ class LinearListLayout(context: Context?) : linearSmoothScroller.targetPosition = position startSmoothScroll(linearSmoothScroller) }*/ - override fun onInterceptFocusSearch(focused: View, direction: Int): View? { val dir = if (orientation == HORIZONTAL) { - if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) return null + if (direction == View.FOCUS_DOWN || direction == View.FOCUS_UP) { + // This scrolls the recyclerview before doing focus search, which + // allows the focus search to work better. + + // Without this the recyclerview focus location on the screen + // would change when scrolling between recyclerviews. + (focused.parent as? RecyclerView)?.focusSearch(direction) + return null + } if (direction == View.FOCUS_RIGHT) 1 else -1 } else { if (direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT) return null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index bdef14b5..5a3e28b4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -22,6 +22,7 @@ import androidx.core.view.isVisible import androidx.core.widget.doOnTextChanged import androidx.lifecycle.ViewModelProvider import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.RecyclerView import com.discord.panels.OverlappingPanelsLayout import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable @@ -531,6 +532,25 @@ open class ResultFragment : ResultTrailerPlayer() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + result_cast_items?.layoutManager = object : LinearListLayout(view.context) { + override fun onRequestChildFocus( + parent: RecyclerView, + state: RecyclerView.State, + child: View, + focused: View? + ): Boolean { + // Make the cast always focus the first visible item when focused + // from somewhere else. Otherwise it jumps to the last item. + return if (parent.focusedChild == null) { + scrollToPosition(this.findFirstCompletelyVisibleItemPosition()) + true + } else { + super.onRequestChildFocus(parent, state, child, focused) + } + } + }.apply { + this.orientation = RecyclerView.HORIZONTAL + } result_cast_items?.adapter = ActorAdaptor() updateUIListener = ::updateUI 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 f9627e46..1ef3cb55 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 @@ -157,6 +157,28 @@ class SettingsAccount : PreferenceFragmentCompat() { ) dialog.dismissSafe() } + + val displayedItems = listOf( + dialog.login_username_input, + dialog.login_email_input, + dialog.login_server_input, + dialog.login_password_input + ).filter { it.isVisible } + + displayedItems.foldRight(displayedItems.firstOrNull()) { item, previous -> + item?.id?.let { previous?.nextFocusDownId = it } + previous?.id?.let { item?.nextFocusUpId = it } + item + } + + displayedItems.firstOrNull()?.let { + dialog.create_account?.nextFocusDownId = it.id + it.nextFocusUpId = dialog.create_account.id + } + dialog.apply_btt?.id?.let { + displayedItems.lastOrNull()?.nextFocusDownId = it + } + dialog.text1?.text = api.name if (api.storesPasswordInPlainText) { diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index a29dc192..5eacdbe2 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -199,17 +199,13 @@ android:id="@+id/result_back" android:layout_width="30dp" android:layout_height="30dp" - android:layout_gravity="center_vertical" android:layout_marginEnd="10dp" - android:background="?android:attr/selectableItemBackgroundBorderless" android:clickable="true" android:contentDescription="@string/go_back" - android:focusable="true" android:gravity="center_vertical" - android:nextFocusDown="@id/result_description" android:src="@drawable/ic_baseline_arrow_back_24" app:tint="?attr/white" /> @@ -385,8 +381,8 @@ @@ -423,11 +419,11 @@ @@ -568,6 +564,7 @@ android:layout_weight="1" android:minWidth="250dp" android:nextFocusLeft="@id/result_movie_progress_downloaded_holder" + android:nextFocusRight="@id/result_bookmark_button" android:nextFocusDown="@id/result_resume_series_button_play" android:text="@string/type_none" android:visibility="visible" /> diff --git a/app/src/main/res/layout/homepage_parent_tv.xml b/app/src/main/res/layout/homepage_parent_tv.xml index d0c88c39..9dcf0bae 100644 --- a/app/src/main/res/layout/homepage_parent_tv.xml +++ b/app/src/main/res/layout/homepage_parent_tv.xml @@ -2,33 +2,30 @@ + android:layout_height="wrap_content" + android:orientation="vertical"> \ No newline at end of file From 0cbee7068326a8f215f53c45d9c85d3601eac468 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Sun, 19 Mar 2023 12:51:54 +0100 Subject: [PATCH 271/273] [skip ci] Update locales.py --- .github/locales.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/locales.py b/.github/locales.py index 9ab272b9..7d6d6b90 100644 --- a/.github/locales.py +++ b/.github/locales.py @@ -56,8 +56,8 @@ for file in glob.glob(f"{XML_NAME}*/strings.xml"): if child.text.startswith("\\@string/"): print(f"[{file}] fixing {child.attrib['name']}") child.text = child.text.replace("\\@string/", "@string/") - with open(file, 'w') as fp: - fp.write('\n') + with open(file, 'wb') as fp: + fp.write(b'\n') tree.write(fp, encoding="utf-8", method="xml", pretty_print=True, xml_declaration=False) except ET.ParseError as ex: print(f"[{file}] {ex}") From 52d495f425fa3a305a2c4018c36b93e9542751b5 Mon Sep 17 00:00:00 2001 From: Osten <11805592+LagradOst@users.noreply.github.com> Date: Tue, 21 Mar 2023 20:50:13 +0000 Subject: [PATCH 272/273] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 3430d626..e3d033ba 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,7 @@ + Download and stream movies, tv-shows and anime + Chromecast -### Screenshots: - - - - ### Supported languages: Translation status - \ No newline at end of file + From 67b0549fd2a3fe4b94d0a6f03f490bfa8956258e Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Tue, 21 Mar 2023 21:01:47 +0000 Subject: [PATCH 273/273] remove images --- .github/downloads.jpg | Bin 59461 -> 0 bytes .github/home.jpg | Bin 139384 -> 0 bytes .github/player.jpg | Bin 49418 -> 0 bytes .github/results.jpg | Bin 98562 -> 0 bytes .github/search.jpg | Bin 152135 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .github/downloads.jpg delete mode 100644 .github/home.jpg delete mode 100644 .github/player.jpg delete mode 100644 .github/results.jpg delete mode 100644 .github/search.jpg diff --git a/.github/downloads.jpg b/.github/downloads.jpg deleted file mode 100644 index ca14a664a2ce2b07cdc366343d54c690d9bacc01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59461 zcmeFZ1zcTAlP|h)hv4q+?v~*0F2UVBK!79!cXxMpw*bN2-7UB~gtwENbG|b(XYPFS z?tL?F?(fuMb=Ru?cU5(D?c{L291VUt z0}f07@&R z3=KmD&IJZS0sut@0Ye6P`3h_RaG*o{81R1(BnT)NI0O_luoMU6H~OOx@Usx|Wf1@m z1_B)IV8HPXa;fl-^8XYE)--dHZlvV_La@4TOx$|Yl#HJbYKC|$r z8+kmcH1jA<%K+@Dac18f+UcSyn2fFwFlycJ8OE1;>apktc+GP5k=?|)K~Q#UyO&3- ziEbJBl^fH)d{N2vCW0LYA^YYc&mlv+J0~WYD|Y2YJ37n|`XPWH4jI&gFde*t8^N_P zUv_2X(*NYGJ-z@;f^ti2cyu;Rl0He$k@;4AGbJ)#?WZ=}v0V6eYJGjl?=Zs=dg3&DSo9sS30rM4ae149=uLeE}ud&~)t$U9lYadTmM}*Z(o0vOjt>|!rA;M@jLBrki zmth{hgY*xzcE|U8xy_U=`Ew2KdkUAw?w|S642~~CNWD|``oKEAq$8hP;f~94y?+6W zA(ke?&@uNslVF*hoHbK=TkNEOhYaza*Hp0kUG-;Da_GU4bKh+4!Z6b28~Z+FptU2= zg(UvgOfVxIiG6$&IvpYiPn|bYEwO+OI}z!79mTmFEwW{NhjFLgw{rj+D+S5MW_`ra za!5*rRDFci0B8V&IlyG3kaZ*TH%-8iLKgM`pTT^-MTLXC+f2jO;sZW`y|vv1lx{!1 z8fKPvtgFk>Hn7M0S+ZH+1K;}yE1BCsd@n>A$E#-` zbmnq+FWE-c11e!@f7S4xG%6bf!nl228J#4Uy1@_HFGmW#h?<(Pg4iYN+3sMlG`+J) zv1DIn=spq(<9|h{k#rz-hFac{w!sf35Wi_bbgw20-~Q#FwDkuJ{(9~Y{y$)Nm$BdM z?{kHcZ8F6KcFB>#{O_`SySV$`J#XJX!>7@8;5HD?8T7mK5zpZT=s4m#{GVn0Jj_xn z@T34RB5xHuEB~sY&<*JinCagGMkt0t7`u<~zWo&lC<7by!W~r)CkH@ze&S!*09#po z_5WpNh>PvX{Xihk|_B?YkTHIG4+1jz8l$&qR zF=2x5h+;fRR=VX$_mT`Lhm_^iiF?1p|GQM5$vKRNbWa5Ll^yq6!EE>CeBJQ5WGP8A z<ja2LW3aZhJs%I zt44y<6_Z0Z#yZ~X|11DWf&OvAz-BIS$vsS4=EBYz{YVo2IhY3knE!EBKp0`3cjz-p z0p4x>AZx1+KlA_H1Oh?@T|xDY>VI)o`2WUl2q~Tx>%K`8@n~cM2@!W<i+fwRyjjV+{mnuE zXna+f!$JV>@J#OP&Vzec$@RsuiMDo1tOjUY){b)-_v43uQm0E3zp2?Z&$}Ke6^s=F z({@?S7WT!a!{p{B)+sw9WlP!6c_*uM>Cz6J19i>p+)TdWxB@}@e^NPO17-PGCe8j< zb}g({gOQPrhbNBaennY=9)I~rfD!J((%hoV0`r>tb_@RFj+afx`@!g$X};|LjEN4? z^0BPq{jK_;^Rjql_*I#A=hC_|%_HkQ(ZensW_nh<3k!4eG9{SUjB_12^^R71b9{b_ zBPBuGvHyuMxpF=GdtoF@6~qwW&9k<7yZT^q zH(_#qW96jNRso*#U27s>Rm3^W+OgcF=#hISE9*nE^8E7EcAi7+_%Q9L#6H{piN2P= z&gerw#wuo`4vc)+F#eEq|2;uBod%YTF^8>bU`t<{cGr6l6gWbr@)1;EX?S)dhVj!T z3gItA?IJw?yUu|)%-lQ)uvqs200bZRwpO2L>`3@N{Bo#%EN^Qh#9IgFk&AUo9defL zYNo*mZzhSpalj=3V{1&(WuefZCj%n+uG(n2{5>xwuV=GoL;C7_T%5je;1BEwq@k$N zBO$>PF75U^*V z8kB~4mNeMenU6G87W!&TZAQFG#*B<0d-}pAYx_$7J@dchP~(DvUV!)C0RkcCF97}X z%W1wBz&PL2Cy>?kn+U1%4zu$QKc46}Zn3YPxX}3lKVmg*k}7P@Md|x z^yUMk(6vMBO3Y>GY(Vb$3g3*jFNRRX#JF*_Y+ZgHzDj7cFSmd`Yx z*gs$J-dDQzL7bCA8qthI6kqGsVvH?sf?$8B`5wno#2FhqNgPQce=rdz%Bgx&{f;1#>Ppv^sv69 z^-wRp-e^Q8NT+r%om^^)pc1=s*;az-5;cx0M-dbM8yEG|98bffz47=(hr4 zQR4~k1UA>Zls>%z#4jYTndy#u($Cw-&kuiv>5ug^Oq`X zy>nyQ(t3AOnmpG+ztgl3tmk#hp-#RuO0!#*XgA=f?PbTu5SP!d|1a(Qo1=uUP~+{M zkXNK*SI?)+m$!P0_j|DFdkRJ+Kc;s8_$NqS85()h`#aT3`}DERUBm9oyPG}0F#zYJ z#H{NzBU3v-^PtjvXgh1$oM6eI6WgTCM*=+M>i-){e=R_K0f4oSl}^@QT}&=6(bJXX z)$3W;Yez3QOg>7fDjgO%Xq9W}lyK^rJm_g#Ca6umQ>B&p?V>6cRVw(4^EOKSNeiS#T+ea20;bB#*_1=00~g|f|-lN^giw#m+%)Oo@~g;sz0 z;P4k#5F>bPDKX7;`|_WiZDkK@EmLMDidtlOQrPB-(i>^4)wTR**ljrW^Al{7V>$MV zEE;Ufa)~^c{zBDXi9l;nLeI-@GulVyZF_6fp$9u{tmhPLSMpp^>ZTeO8+5VDx+R^I zVOf0rc9o_1&ixDLhksG=?}NW1@OK3Mj=ka)0O8AjyASB?L$sdK>NGQ5zi*3gL!F1LHOg3D|AliPN{uoTAyk90f1IR-lW0&3l1qvD4(()1iS5_0zYotoB0#} zBZ1G0DGPi~(Pb1YmCQjcQz9rs`=iW+Y{7LsXyRL+H2=o|7u3LCC7_4LE%t1{iDlka zhEuqu59iLn3VqULJFkCM`slM4#tr)(3S4ji2Yq9a56eV`7&r(Xtc#C zS19isv8i(p<-_l#oVAwH8{hpvWDGoU+a$8hf$w|$p!P%Ceb5g8__~Iw!yfmv?{*Ct z6mbRpN5G00+Agi%UjIa0Q7xx&soU?V!o)alri_f#`q7L%#eA>6CUwWd zj6UJjZyIpb`?)Q8)$?2Hb-Pm+8>ehdxm!5;qv_Vh6@DN16JtIJB%xYI2H(%?#J|DlKWNu+CkY+DtimJ7};of7FTbDI1bgHsbllksE~UcFO&< znvXtV_bt|iJxpw-_xD*!8G}g?Ymse#Z!@9%K32QWQtnq%JS})jC3(3M=flnH5>gj}BuVwDet=ll$C^@kSB8L$NC-kZ*!1%BN$Y3 z*X^$n>oH<-M85$*MK!k9ee>~r!$?==xslg@6N6ts2@p|Ne$MY^Lz;XV|4sUr8R8o0 zKdkOM=NRRU{Eh>0$>9Zzeqk~CUGQI-1KgMdJT0e(jN=lh??km#>a zP|?_l*k0>15o3^&F|%?g*pq*VB*|O{z8VS*e4i8q0`vutQ$?#Ahw6)tsz;ic7R0nI zdN)qO%^`lYw0^Z5N>AZ;W>Lqp6G}DKM%tmLj?JK>5mvte!VzP@u+hl1@dlaZZ4rrV zU=($-zorAGf`#Ikg}`Bbf|$={0(NF(&8gm#Qu;M&420LQ2N-e0I&2-0 zma_x5#dJFzCcLZukp(*6qzk;4b2S#^KOLc;`9kbRjF=zN?N)xBe*+QYhZiQ81;aeK z!IS!4!XuA8VD zgPNr!;rZn`!Jo4lqg9foFAQSe2|+3%H4raFQZ>o=H(?)?sx<>cV;H4ILQ+-uke0s? zCyHDk$6tb?R~xh_7uj7kr;OAZS2RAri4Lzm^E!H@dR1I@E%-Ba&wf6fQ?`(HW!_mf zSQ#^8OI5x70o@{$b$bA`!yJQb>}T?4^=&^-=2aIP5^_=2W3fuzIL!`skjBn?{2ZQ7 zoOXtLXUs0*v1Q+3(<|~_F==aUb!%_-Nc|HoaBOKtb+AH0e1PG>S7v-mBlBfL%1U8v zh+El@Fdjv)>EPb8vY!)WTAiG$!9e0L-|Sid-;L3yq`A$8bmpL~@;a-lAd!BsG8x&U zt-Gzy@CBe;%9hYpQYDm3wZvW)9o+&ViQ1CkK--d0$nPI>!Qi6=J|nE69>dPEIhJ09 z)E8KQC1T>gRBMP@%9SoU^Vp=fn96R~HdP`VRwYf%!YK<-Lpuv ziU5vYzi35m;IoPIw3gg$D-l0qQQ_P8ucL(hZ&I%~cUzhkWW53vJ|RKY70F)stK?4~ z)SMfvQD(8+^Mi;R2cP8(_^(B1BIIum7EVW0AANkSnEbI?{MCX%VSL@tr**6EJfHa{ zOZTaB%InHeR!!n}IO(WDNo6`g??#(o;uOhF`L39wCHmTTe2M`ITFv-i6HasLKySifQ_Y1 zFCBWLkn4fUA*BW1TCd*7#|h=Xezr`2>)Sa{!L^3@ z)?rIBS>-DuTjPh3d z6wjLP%`Aa&5wV-HH3`>O|0V5USKi^zY+FIctNXY}uUrC>h!+NS$}Y4*hNr57Fx1>o z=2Oi0n~rKU%%l#jEM$AVri&>2oOSk_yGKwAC6#1C!}4|BJ-Rg)OXf+05L>XY2-fPo z+pqD&DRwW{!bpj~Fw>EbKJpD?fR}pi=`Jy7v>k4UqeeBoVOP1-sildDR8|V9R;>XK zu@T+X(n@yV7~iFstb=DI=vAtH)>5^xVtEkP>_c@>`WhXO|9D?Rj>0hQR)ug=x_4|Q zZeA2!l1(8xx!lI-0&ZL8w#eA(avMJRR_vp3h-dTdeB^nD1qQ!Mv7f1@f!9`TLe%65 zS-i?DY?Mv-p`v?oT;=k9EaP>4W=UZ&4!1LVu&QkBAU3`kt}MSy#j!5ip^avLLA8f& z)y)itJ*RL^i3+y@IdMn;?Fr1#yGfGXN1+vmuVr(1(sGT1pSIgKakWyEqxGC455scl zf0OPA)Z(6EIaoOuG%?o3QBCKkYD+ovH0N)(Ppah6iyiuppX55?mg96F?wtc_IF=*mQK`@`U66@CDz?rC=4fC(HX3sGw!~Jg+VzM*k z^d2W8%!zW`%;(qybNo+8=(MGk`L)bOUI2;H0>kXjjM>`mNNP!!KD+ElH3&Y^-{Mz= zXY${#-G>Pub?Z$&kT*nLE4D9N$v)KD&E$BO;a;1sm54oSaUM)`sD=Hv3PV!Nl}O#_ zA&(4WJYyA`L~;KoBp3|j3mG@I)r_8;HwxZ^NDFUVRO{dODo#69<3}ES_U92KX(x2$ zFsMODls0RKy0T&2y*qr1Bi$dQw^%0jB(4n_4%z+&PFFq^? zAIihF&plgMjw^lPdj^IijXUz8Cy0o@B8=KO0$U|2y;Yku6vHH6f3kFcd~QsdShs}2 z)b|iOA{w%CiuC8)c!UI52Bt$xN7mz9JWcQjQ8u(z$M^s;j>a?U0*$^XJoHu}TH%Ol zaYJ?;!9_|Z^Ct_$8uor1+vfh`x6upilux!vm+xIF5XiS>DxCyn>Eu%L^-M)dn&>sJ z?TB4O+fpix1?hJ8vkEr5T-4v(9F4^V@dyLUO|wa8@2#*1*u<6PN1CE)YZnKBAzt4EG-D~1hjO)gm^TiEmV=#%GTRrp+z|<#!t%mF(gI#iPJVZU!|^jSk8V0 z0IkdUvSJEXW{AtfGD=>?lvgcHYw%{NQP>UNMid*lv^a9`h@J0UX?3@k+1_Z7P72n! z@oa-@K5<#^2`vO3!G-Sh>Wyg;snVg&r%x2LWOJ8b)7V>TUvUWs0O;cV(WHxmLx-TJoB;q^3G3z%f-zbu$|wcXPY(6*1lAT!uM{? zqoP^xChaHx3JK|;3D0|p*umOoncc0N_#ja5n@#EM?BO1!f;)~Hn?7>Y`4H;!N0toG zF1Dr#(oU|4;18wPhLP%8#LN%Qnl;tJ!(!e|2c~4I>QHL7+}}A^PsBXEt8uR**GfMx zT$nylq-rxzjYvH~D`x2qpfK_AQX+OHFta$uQES)j?tUNJV$c%EwdLHJ<_z`(!&;T1jy{Y;sa<=`kN zC~zk;Z(jgYQIV!-2Cwg4Nk}kCdJPt=oiphVV*@RqA$tIU@_hg*EF^sI zn?$DfK)slFpO{{M^y(_;w`a=C%zQ5&1VmmTe@>KOY*cYaghWF_Lz?&)4i1f(8%owC zgE>d6Jd%htX^FHSSSB8zFkP^1Aqj&@G~n+OpVNQ#Q-wVf6Z4mk9ui$LWSyW7!_bUl z9`)hm9@gw^YG4o1o#Q*HdoUp|G7Jobi3^?0zJ_WK77Z}7vr`%28U4OMgYblS@bF;7 z;SgUs&xm4Rm?GcS$BdpH75d2V#kNi^IPKR(;#bB!;iG%iwT7TS6tROvg>P1+n3vbr zr#`8C7r_F0VT1?$>dO}vEUZoayIdk*GmC-o8>MfoySd2bFEPsMuc7v|9Q1e|6tZG< z4rB7N3MLmaEUr7mM3QAOv{onXe;|>Vc5%?kFR8C|>{VZ%!stVP*3%xH+swZp>huGH z6b8eC3x|Y+vdD@F_axPDKSGP>#I7l5_-q`-a0DZl>3sI_WJv$VYP zVy2OisaQc0hT-?1+^ky+o>8~MUW&8?8n(L0V(fpGRE1;E84=mf5Sch~F1FrwQ^Pao<0!mrmhZA+Wb6~`-(Rp&<2Loiu_J@W zVtNlxxWSb9V^9F&HIwDNCHp}24|H$P`vjZ?!7tx&^nhRi3V6bGCjEYWWbn_QKS5lZ z`nc!wqY>b$xUW!duqPyJ$>_{3&c6V*IuID6ne;n-H}zitAil&OKZrno1O41ADIQrr(zV++V%{Ccd1G?x{eoUh?f?N}t#OKEuuhk+A1AEp>Z z489yVTr7Muo)XYqwNpVdaD1MVZB1fQ1&L`d3QD`(t=FsbM#cpC`r8t9qOC}JnL<^d zzw&oRX)gImN%Q%bUf*uZ2;ST$it39e=NNqLF#FR}K?KGapoo@jHW@e ztWG)AdI5xew0r0-30aX$!)$Z-N?m|saUU)zZ>EOg$1kcj6_WfAq+a5{FyaRxotnfy z$adShU<{{BLHYs+nQ*)(v%>PCZnbj3dsoRop6?Py|M-gbv>?0J4xbn)3-m_a-Qv!I zFOf;V7ZMUAk_A$j;1f6#6BD->$SV@yPqZk0`Y?ntt93K>nP^(zT0%=&-gD88?6BBy zbkftdy6;2Ljpzd$UU@0uSsWg9YYeo7I;v3(^&{0tF}}BJ;?uLI|HG`+F9pQJrh(OI z!A+`p3S*^2}%qPshaQ>E0Cq z-7^9M0|WG9gAo}6PXw<>uznHv>yIAMrZKc{jYpEV%P~+|`pR}(uq)yg+7P}kN*^Ay z;>Hbuwp)nM4}3x;=cfL;mF%1odkSrLGpVPiy1}4sKP<^0YJVK~v9ZR!%sqy~D%w}6 zS!KA>nndb&;fKm5aa7ZC^}7{*YnQPV)jAsjVMH;v8Fj7akhQJ} zH4Pa@=ZYTj9WHEmAOZb^{}}n@3kmq^K;$0+AbD9v?=Nm?+cT1kO=l@5aT6<3 zm1LIY2tg0=I-eAiBQ6y0Js@(c z$SEW~Xanoua;A(`YK?FL{*Gth*}z2i5=UHd$&Ck@Ym(2xd!;>f?XzCAeEpNzS+Oxk zcm1nDEnJJ4)+OXDwz!^T<*j1eIt6&Y;rOtMI9N-PeA1P#??P`;fKMf`8N>#xwWx$5 zeouwsK?&R5!O_EyQ{O2!FgI-=HQ!}iW;Oq(a?;2) zE@|F-$wxLuA35l3rUfW(vqKGke`B121t%0)j*cuFE9+VDLy^$5udNUsx#L9(754TE z0OpA%@wol?b624MiUShIlSLUhvH{vXY~r=bC9@NDw$;w`-NQE}oTNes`G|5q;-X~V z-LBZiavvD|m;yVhAPg+g63Zwvu#JRGVQEd*lW^bp;}xJu9|qRm?p~RG3r4Z`8ftm* zC0Vzo(Gei|(Ook(log~qUu%xHPD#4VTuC%<&Tpd(SX=I&%|McpJ|&-v-G>R}Ppy=? zS>Z2sm*W=PA#FO*#-ey8z9MlAOl^0!P>TBg+0xlRs!0m!A!CX4_Wf_;^0({-u-t9Z zrH{*iO2{3L|9&<265>wRq&MnJqS~t_%ch7hNJ2?^I+j}WOu>&UuQU4dLvFV#uMcp@ zK@*;Zu{Ee{QzPW%PalSLM{cAb#*_<1E`sVyDyjK#R0Ln7npP#9WDzrw710W%C@XC9 zBqX}{9vGFeUW0ra zw2N=N^kH|o$N7`@C~lC4;=sOZ1yr`c;G5Uc7RM|5EZr3;=jle9^%vNMr}L=G(sk1I z8|q@|kG5AqB?FiECpxj!CB?A^d2>v<2ifyD$2dxv?=@o5LQxd0A%qD#RJ6f#b7)jh z4CQ5IixgnC43is@RR&mB9p-k6?k>rw1JN1juir!Zl(8;jUTR4?=|t91#m$B8y#Tnv z2t{G6?&b&g;tQ79WuBGpok*1P{Y0H-^R=A{V+&*R19zKdvht1koOF@*bQ7Up<|1T9#E=07T3tf>m24mV9=)U`WOXO@(J zs(XC(348&3M!FM|ZK}d8r%>sB9vL2U1&WF;5G15oZ)!A0!nt4+OTF^NoZO|8 z-Ard$%9|^`WW+F`Sc>%R%PA;$`$H;*n)R_+{tA9E7ROWf>qQAh z6ctfQ`KF!o=7|lNHTcP!llr2@I&Xf%yc=+qc9dXtk{~JO|7-I z2@9*Ls^{SNT5OvlC}|KtsEs}>BC_N{rI?f!HWqz1p{WwECn@R#rxl;+S5)^VIs}89 zrSSC0oB(M;dP^V*HcJ|5z6u$I&{3!dv!m%k4_B_?VaRzcs+Y74h9{_FZtL}#cQEiX zG1r;?tu|V&$xVKA9jdlxpz1}R>rfNlP0^TX!d$yBu+9hS_hys8iAf!4kj^EMQ(jGJ^F(?DC4&zWppy6m8PKL0fg@6D zL~yOf{t28OtCziXChS!d4kJcDtLjIzka?qJ>WmD-Qs~^b-2~@qY(h6!x4~7}HyXgS z^jn>WB=3mEx%RWhW!g=ty;M~v0T+ELp^sm2FJ2Qv0 zUN`LJKV`tD#L@eyKCq%%rL{gVH^N3e1G{2@)a7o z{l_nzKLZQ;Be2VCmE^}wz3A_`v)zw)=Ckh*R2{HuyXEz&pv)$X_N6}+^7a|rm=&q= znHh}tw)@&xGQ{v6ga{V{)c3FWXF@wC3nZFy3=E#FynO=9Jp!zWj1u({OHR=aX7GqI zGO-l0g#2L9s1$NEz)YmlKD)l2htfxio*D!@_IC&FcN}Za)ADCwQ zlu{>G3#e z%=#w#6$uNQqQS?i2?#VI($_*t4vtY-olFY)hW1XMs?VY~$b@6i*_A&;=8zltc1@mV zu5bRf>m5>jRWp=nWoIuiqUr1h|y(jn#E4I71pxO_BWFaMETt2 zFOu7EZz!+Iz%wRmQGBGvC7)@5hs6V^i`)_q@e>K<7qMA8#r5Ihf;VBklKAu(1+Wh- z=-&>}#9{5kEnAl~DPw5Vq?_&640Z^=C{qB}OYiyynrℜ5F2>W2mS%`!tt>Y2ggw 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