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 01/16] 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 02/16] 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 03/16] 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 04/16] 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 05/16] 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 06/16] ?? --- .../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 07/16] 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 08/16] [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 09/16] 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 10/16] 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 11/16] [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 12/16] 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 13/16] 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 14/16] [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 15/16] 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 16/16] 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 @@ 在搜索结果中隐藏选中视频画质 自动更新插件 + 自动下载插件 显示应用更新 启动时自动搜索更新 更新至预览版