Merge branch 'recloudstream:master' into arabicLanguage

This commit is contained in:
SANCTI-afk 2022-12-01 23:36:30 +02:00 committed by GitHub
commit 96a447c302
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1014 additions and 141 deletions

View file

@ -20,6 +20,8 @@
***The list of supported languages:***
* 🇱🇧 Arabic
* 🇧🇬 Bulgarian
* 🇨🇳 Chinese Simplified
* 🇹🇼 Chinese Traditional
* 🇭🇷 Croatian
* 🇨🇿 Czech
* 🇳🇱 Dutch

View file

@ -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")

View file

@ -110,6 +110,17 @@
<data android:scheme="cloudstreamrepo" />
</intent-filter>
<!-- Allow searching with intents: cloudstreamsearch://Your%20Name -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="cloudstreamsearch" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />

View file

@ -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)
@ -146,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)

View file

@ -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 ->
@ -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
}

View file

@ -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,17 +42,21 @@ 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.loadAllOnlinePlugins
import com.lagradost.cloudstream3.plugins.PluginManager.loadSinglePlugin
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.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 +90,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
@ -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<Intent>? = 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
}
val VLC = ResultResume(
open fun getDuration(intent: Intent?): Long {
return defaultTime
}
}
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)
@ -188,6 +214,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 +241,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 +279,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)
@ -553,7 +595,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)
}
}
@ -619,6 +670,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()

View file

@ -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<ExtractorLink> {
val extractedLinksList: MutableList<ExtractorLink> = 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<ResponseLinks?>(src)?.let { voelink ->
//Log.i(this.name, "Result => (voelink) ${voelink}")
val linkUrl = voelink.url
val linkLabel = voelink.label?.toString() ?: ""
parseJson<ResponseLinks?>(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()
}
}

View file

@ -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()
}
}

View file

@ -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()

View file

@ -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,10 +223,8 @@ object PluginManager {
fun updateAllOnlinePluginsAndLoadThem(activity: Activity) {
// Load all plugins as fast as possible!
loadAllOnlinePlugins(activity)
afterPluginsLoadedEvent.invoke(true)
val urls = (getKey<Array<RepositoryData>>(REPOSITORIES_KEY)
?: emptyArray()) + PREBUILT_REPOSITORIES
@ -265,7 +267,8 @@ object PluginManager {
}
main {
createNotification(activity, updatedPlugins)
val uitext = txt(R.string.plugins_updated, updatedPlugins.size)
createNotification(activity, uitext, updatedPlugins)
}
// ioSafe {
@ -275,6 +278,87 @@ object PluginManager {
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<String>()
val urls = (getKey<Array<RepositoryData>>(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<String>
uitext: UiText,
extensions: List<String>
): 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()

View file

@ -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

View file

@ -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(

View file

@ -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?.isClickable = !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

View file

@ -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) {

View file

@ -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<TvType>().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")

View file

@ -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"),

View file

@ -8,6 +8,8 @@ import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
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 +47,15 @@ class PluginsFragment : Fragment() {
pluginViewModel.languages = listOf()
pluginViewModel.search(null)
// Filter by language set on preferred media
activity?.let {
val providerLangs = it.getApiProviderLangSettings().toList()
if (!providerLangs.contains(AllLanguagesName)) {
pluginViewModel.languages = mutableListOf("none") + 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

View file

@ -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">
<argument
android:name="search_query"
android:defaultValue=""
app:argType="string" />
</fragment>
<fragment
android:id="@+id/navigation_downloads"

View file

@ -62,61 +62,7 @@
<string name="sort_apply">Appliquer</string>
<string name="sort_cancel">Annuler</string>
<string name="player_speed">Vitesse de lecture</string>
<string name="subtitles_settings">Paramètres de sous-titres</string>
<string name="subs_text_color">Couleur du texte</string>
<string name="subs_outline_color">Couleur de la bordure exterieur</string>
<string name="subs_background_color">Couleur de fond</string>
<string name="subs_window_color">Couleur de la fenètre</string>
<string name="subs_edge_type">Type de bordure</string>
<string name="subs_subtitle_elevation">Élévation des sous-titres</string>
<string name="preview_background_img_des">Aperçu de l\'arrière-plan</string>
<string name="subs_font">Police</string>
<string name="search_provider_text_providers">Rechercher en utilisant les fournisseurs</string>
<string name="search_provider_text_types">Rechercher en utilisant les types</string>
<string name="benene_count_text">%d Benenes données au dev</string>
<string name="benene_count_text_none">Aucune Benenes donnée</string>
<string name="subs_auto_select_language">Sélection automatique de la langue</string>
<string name="subs_download_languages">Télécharger les langues</string>
<string name="subs_hold_to_reset_to_default">Maintenir pour réinitialiser les valeurs par défaut</string>
<string name="continue_watching">Continuer à regarder</string>
<string name="action_remove_watching">Supprimer</string>
<string name="action_open_watching">Plus d\'info</string>
<string name="vpn_might_be_needed">Un VPN peut être requit pour que ce fournisseur fonctionne</string>
<string name="vpn_torrent">Ce fournisseur est un torrent, un VPN est recommandé</string>
<string name="torrent_plot">Description</string>
<string name="normal_no_plot">Aucune description trouvée</string>
<string name="torrent_no_plot">Aucune description trouvée</string>
<string name="picture_in_picture">Lecteur en mode Picture-in-Picture</string>
<string name="picture_in_picture_des">Continuer la lecture dans une fenêtre miniature en superposition sur d\'autres applis</string>
<string name="player_size_settings">Bouton de redimensionnement du lecteur</string>
<string name="player_size_settings_des">Supprimer les bordures noires</string>
<string name="player_subtitles_settings">Sous-titres</string>
<string name="player_subtitles_settings_des">Paramètres des sous-titres du lecteur</string>
<string name="eigengraumode_settings">Vitesse de lecture</string>
<string name="eigengraumode_settings_des">Ajouter l\'option de vitesse sur le lecteur</string>
<string name="swipe_to_seek_settings">Balayer pour avancer rapidement</string>
<string name="swipe_to_seek_settings_des">Balayer vers la gauche ou la droite pour contrôler le temps du lecteur vidéo</string>
<string name="swipe_to_change_settings">Balayer pour changer les paramètres</string>
<string name="swipe_to_change_settings_des">Balayer sur le coté droit ou gauche pour changer le niveau de luminosité ou de volume</string>
<string name="double_tap_to_seek_settings">Taper deux fois pour rechercher</string>
<string name="double_tap_to_pause_settings">Taper deux fois pour mettre en pause</string>
<string name="double_tap_to_seek_settings_des">Taper deux fois sur le coté droit ou gauche pour avancer ou reculer</string>
<string name="search">Rechercher</string>
<string name="settings_info">Informations</string>
<string name="advanced_search">Recherche Avancée</string>
<string name="advanced_search_des">Donne les résultats séparés par les fournisseurs</string>
<string name="bug_report_settings_off">N\'envoyer les données que lors d\'un crash</string>
<string name="bug_report_settings_on">N\'envoyer aucune données</string>
<string name="updates_settings">Afficher les mises-à-jour de l\'application</string>
<string name="updates_settings_des">Chercher des mises-à-jour automatiquement au démarage</string>
<string name="uprereleases_settings">Mettre à jour vers une version bêta</string>
<string name="uprereleases_settings_des">Rechercher pour une mise à jour vers une version bêta au lieu des version complètes seulement</string>
<string name="github">Github</string>
<string name="lightnovel">L\'application Light Novel par les mêmes devs</string>
<string name="anim">Application d\'animés par les mêmes devs</string>
<string name="discord">Rejoindre le serveur Discord</string>
<string name="benene">Donner une benene aux devs</string>
<string name="benene_des">benenes données</string>
<string name="app_language">Language de l\'application</string>
@ -192,16 +138,12 @@
<string name="primary_color_settings">Couleur principale</string>
<string name="app_theme_settings">Thème de l\'application</string>
<string name="player_speed_text_format" formatted="true">Vitesse (%.2fx)</string>
<string name="use_system_brightness_settings">Utiliser la luminosité du système</string>
<string name="category_general">Général</string>
<string name="dns_pref">DNS avec HTTPS</string>
<string name="display_subbed_dubbed_settings">Afficher les animés en Anglais (Dub) / sous-titrés</string>
<string name="phone_layout">Disposition en mode téléphone</string>
<string name="app_dub_sub_episode_text_format">%s Ep %d</string>
<string name="rated_format" formatted="true">Note: %.1f</string>
<string name="subs_font_size">Taille de la police</string>
<string name="use_system_brightness_settings_des">Utiliser la luminosité du système dans le lecteur de l\'application au lieu d\'un écran noir</string>
<string name="show_fillers_settings">Afficher les épisodes spéciaux pour les animés</string>
<string name="resize_zoom">Zoom</string>
<string name="resize_fit">Adapter à l\'écran</string>
<string name="app_layout">Disposition de l\'application</string>
@ -211,27 +153,8 @@
<string name="automatic">Auto</string>
<string name="cast_format">Acteurs: %s</string>
<string name="duration_format">%d min</string>
<string name="search_hint_site">Rechercher sur %s...</string>
<string name="search_hint_site">Rechercher sur %s</string>
<string name="type_re_watching">À re-regarder</string>
<string name="sort_copy">Copier</string>
<string name="sort_close">Coller</string>
<string name="sort_clear">Effacer</string>
<string name="sort_save">Enregister</string>
<string name="subs_import_text">Importer des polices en les plaçants dans %s</string>
<string name="provider_info_meta">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.</string>
<string name="show_log_cat">Afficher les logs 🐈</string>
<string name="chromecast_subtitles_settings">Sous-titres Chromecast</string>
<string name="chromecast_subtitles_settings_des">Paramètres des sous-titres Chromecast</string>
<string name="double_tap_to_pause_settings_des">Taper au milieu pour mettre en pause</string>
<string name="episode_sync_settings">Mettre à jour la progression de visionnage</string>
<string name="episode_sync_settings_des">Synchroniser automatiquement votre progression de l\'épisode actuel</string>
<string name="restore_settings">Restaurer les données sauvegardées</string>
<string name="backup_settings">Sauvegarder les données</string>
<string name="restore_success">Fichier de sauvegarde chargé</string>
<string name="restore_failed_format">Échec de la restauration des données depuis le fichier</string>
<string name="backup_success">Restauration des données réussie</string>
<string name="backup_failed">Permission d\'accès au stockage manquante</string>
<string name="backup_failed_error_format">Erreur pendant la sauvegarde %s</string>
<string name="no_episodes_found">Aucun épisode trouvé</string>
<string name="documentaries">Documentaires</string>
<string name="ova">OVA</string>
@ -289,8 +212,117 @@
<string name="actor_background">Arrière plan</string>
<string name="home_source">Source</string>
<string name="home_random">Aléatoire</string>
<string name="coming_soon">À venir ...</string>
<string name="coming_soon">À venir </string>
<string name="poster_image">Image de l\'affiche</string>
<string name="authenticated_user">Connecté %s</string>
<string name="action_add_to_bookmarks">Définir le statut de visionage</string>
<string name="sort_copy">Copier</string>
<string name="sort_close">Fermer</string>
<string name="sort_clear">Vider</string>
<string name="sort_save">Enregistrer</string>
<string name="subtitles_settings">Paramètres des sous-titres</string>
<string name="subs_text_color">Couleur du texte</string>
<string name="subs_outline_color">Couleur des contours</string>
<string name="subs_background_color">Couleur d\'arrière-plan</string>
<string name="subs_window_color">Couleur de la fenêtre</string>
<string name="subs_edge_type">Type de bordure</string>
<string name="subs_subtitle_elevation">Elevation des sous-titres</string>
<string name="subs_font">Police</string>
<string name="subs_font_size">Taille de la police</string>
<string name="search_provider_text_providers">Recherche par fournisseur</string>
<string name="search_provider_text_types">Recherche par types</string>
<string name="benene_count_text">%d Donner une banane aux devs</string>
<string name="benene_count_text_none">Aucun Bananes donné</string>
<string name="subs_auto_select_language">Sélection automatique de la langue</string>
<string name="subs_download_languages">Télécharger les langues</string>
<string name="subs_subtitle_languages">Langue des sous-titres</string>
<string name="subs_hold_to_reset_to_default">Maintenir pour rétablir les valeurs par défaut</string>
<string name="subs_import_text" formatted="true">Importez des polices en les plaçant dans %s</string>
<string name="continue_watching">Continuer à regarder</string>
<string name="action_remove_watching">Retirer</string>
<string name="action_open_watching">Plus d\'informations</string>
<string name="action_open_play">@string/home_play </string>
<string name="vpn_might_be_needed">Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement</string>
<string name="vpn_torrent">Ce fournisseur est un torrent, un VPN est recommandé</string>
<string name="provider_info_meta">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.</string>
<string name="torrent_plot">Description</string>
<string name="normal_no_plot">Aucune trace trouvée</string>
<string name="torrent_no_plot">Aucune description trouvée</string>
<string name="show_log_cat">Afficher le logcat 🐈</string>
<string name="picture_in_picture">Picture-in-picture</string>
<string name="picture_in_picture_des">Poursuite de la lecture dans un lecteur miniature au-dessus d\'autres applications</string>
<string name="player_size_settings">Bouton de redimensionnement du lecteur</string>
<string name="player_size_settings_des">Supprimer les bordures noires</string>
<string name="player_subtitles_settings">Sous-titres</string>
<string name="player_subtitles_settings_des">Paramètres des sous-titres du lecteur</string>
<string name="chromecast_subtitles_settings">Sous-titres Chromecast</string>
<string name="chromecast_subtitles_settings_des">Paramètres des sous-titres Chromecast</string>
<string name="eigengraumode_settings">Mode Eigengravy</string>
<string name="eigengraumode_settings_des">Ajout d\'une option de vitesse dans le lecteur</string>
<string name="swipe_to_seek_settings">Balayez pour chercher</string>
<string name="swipe_to_seek_settings_des">Balayez vers la gauche ou la droite pour contrôler le temps dans le lecteur vidéo.</string>
<string name="swipe_to_change_settings">Balayez pour modifier les paramètres</string>
<string name="swipe_to_change_settings_des">Glissez sur le côté gauche ou droit pour modifier la luminosité ou le volume.</string>
<string name="autoplay_next_settings">Lecture automatique du prochain épisode</string>
<string name="autoplay_next_settings_des">Démarrer l\'épisode suivant lorsque l\'épisode en cours se termine</string>
<string name="double_tap_to_seek_settings">Double tape pour chercher</string>
<string name="double_tap_to_pause_settings">Double tape pour mettre en pause</string>
<string name="double_tap_to_seek_amount_settings">Player seek amount</string>
<string name="double_tap_to_seek_settings_des">Tapez deux fois sur le côté droit ou gauche pour aller en avant ou en arrière.
</string>
<string name="double_tap_to_pause_settings_des">Tapez au milieu pour mettre en pause</string>
<string name="use_system_brightness_settings">Utiliser la luminosité du système</string>
<string name="use_system_brightness_settings_des">Utiliser la luminosité du système dans le lecteur d\'applications au lieu du
sombre
</string>
<string name="episode_sync_settings">Mise à jour de la progression de la veille</string>
<string name="episode_sync_settings_des">Synchronisation automatique de la progression de votre épisode en cours</string>
<string name="restore_settings">Restaurer des données à partir d\'une sauvegarde</string>
<string name="backup_settings">Sauvegarde des données</string>
<string name="restore_success">Fichier de sauvegarde chargé</string>
<string name="restore_failed_format" formatted="true">Échec de la restauration des données du fichier %s</string>
<string name="backup_success">Données stockées avec succès</string>
<string name="backup_failed">Permissions de stockage manquantes, veuillez réessayer</string>
<string name="backup_failed_error_format">Erreur de sauvegarde %s</string>
<string name="search">Recherche</string>
<string name="category_account">Comptes</string>
<string name="category_updates">Mises à jour et sauvegarde</string>
<string name="settings_info">Info</string>
<string name="advanced_search">Recherche avancée</string>
<string name="advanced_search_des">Vous donne les résultats de la recherche séparés par fournisseur</string>
<string name="bug_report_settings_off">Envoi de données uniquement en cas d\'accident</string>
<string name="bug_report_settings_on">N\'envoie aucune donnée</string>
<string name="show_fillers_settings">Afficher les épisodes spéciaux pour les animés</string>
<string name="show_trailers_settings">Montrer les bandes-annonces</string>
<string name="kitsu_settings">Montrer les affiches de kitsu</string>
<string name="pref_filter_search_quality">Masquer la qualité vidéo sélectionnée dans les résultats de recherche</string>
<string name="automatic_plugin_updates">Mises à jour automatiques des plugins</string>
<string name="updates_settings">Afficher les mises à jour de l\'application</string>
<string name="updates_settings_des">Recherche automatique de nouvelles mises à jour au démarrage</string>
<string name="uprereleases_settings">Mettre à jour vers une version bêta</string>
<string name="uprereleases_settings_des">Recherche pour une mise à jour vers une version bêta au lieu des version complètes seulement</string>
<string name="github">Github</string>
<string name="lightnovel">Application Light Novel par les mêmes devs</string>
<string name="anim">Anime app by the same devs</string>
<string name="discord">Rejoignez le Discord</string>
</resources>

View file

@ -0,0 +1,591 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %s</string>
<string name="download_size_format" formatted="true" translatable="false">%s / %s</string>
<string name="episode_name_format" formatted="true" translatable="false">%s %s</string>
<string name="ffw_text_format" formatted="true" translatable="false">+%d</string>
<string name="rew_text_format" formatted="true" translatable="false">-%d</string>
<string name="ffw_text_regular_format" formatted="true" translatable="false">%d</string>
<string name="rew_text_regular_format" formatted="true" translatable="false">%d</string>
<string name="rating_format" formatted="true" translatable="false">%.1f/10.0</string>
<string name="year_format" formatted="true" translatable="false">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s 共 %d 集</string>
<string name="cast_format" formatted="true">演員:%s</string>
<string name="next_episode_format" formatted="true">第 %d 集即將發佈於</string>
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">封面</string>
<string name="search_poster_img_des">@string/result_poster_img_des</string>
<string name="episode_poster_img_des">劇集封面</string>
<string name="home_main_poster_img_des">主封面</string>
<string name="home_next_random_img_des">隨機下一個</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">返回</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">更改片源</string>
<string name="preview_background_img_des">預覽背景</string>
<!-- TRANSLATE, BUT DON'T FORGET FORMAT -->
<string name="player_speed_text_format" formatted="true">速度(%.2fx</string>
<string name="rated_format" formatted="true">評分:%.1f</string>
<string name="new_update_format" formatted="true">發現新版本!\n%s -> %s</string>
<string name="filler" formatted="true">填充</string>
<string name="duration_format" formatted="true">%d 分鐘</string>
<string name="app_name">CloudStream</string>
<string name="play_with_app_name">使用 CloudStream 播放</string>
<string name="title_home">主頁</string>
<string name="title_search">搜尋</string>
<string name="title_downloads">下載</string>
<string name="title_settings">設定</string>
<string name="search_hint">搜尋…</string>
<string name="search_hint_site" formatted="true">搜尋 %s…</string>
<string name="no_data">無資料</string>
<string name="episode_more_options_des">更多選項</string>
<string name="next_episode">下一集</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">類型</string>
<string name="result_share">分享</string>
<string name="result_open_in_browser">在瀏覽器中打開</string>
<string name="skip_loading">跳過載入</string>
<string name="loading">載入中…</string>
<string name="type_watching">正在觀看</string>
<string name="type_on_hold">暫時擱置</string>
<string name="type_completed">觀看完畢</string>
<string name="type_dropped">放棄觀看</string>
<string name="type_plan_to_watch">計畫觀看</string>
<string name="type_none"></string>
<string name="type_re_watching">重新觀看</string>
<string name="play_movie_button">播放電影</string>
<string name="play_livestream_button">播放直播</string>
<string name="play_torrent_button">播放種子</string>
<string name="pick_source">來源</string>
<string name="pick_subtitle">字幕</string>
<string name="reload_error">重試連接…</string>
<string name="go_back">返回</string>
<string name="play_episode">播放劇集</string>
<!--<string name="need_storage">允許下載劇集</string>-->
<string name="download">下載</string>
<string name="downloaded">已下載</string>
<string name="downloading">下載中</string>
<string name="download_paused">下載暫停</string>
<string name="download_started">下載開始</string>
<string name="download_failed">下載失敗</string>
<string name="download_canceled">下載取消</string>
<string name="download_done">下載完畢</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">播放</string>
<string name="error_loading_links_toast">載入連結錯誤</string>
<string name="download_storage_text">內部存儲</string>
<string name="app_dubbed_text">配音</string>
<string name="app_subbed_text">字幕</string>
<string name="popup_delete_file">刪除檔案</string>
<string name="popup_play_file">播放檔案</string>
<string name="popup_resume_download">繼續下載</string>
<string name="popup_pause_download">暫停下載</string>
<string name="pref_disable_acra">禁用自動錯誤報告</string>
<string name="home_more_info">更多資訊</string>
<string name="home_expanded_hide">隱藏</string>
<string name="home_play">播放</string>
<string name="home_info">資訊</string>
<string name="filter_bookmarks">篩選書籤</string>
<string name="error_bookmarks_text">書籤</string>
<string name="action_remove_from_bookmarks">移除</string>
<string name="action_add_to_bookmarks">設定觀看狀態</string>
<string name="sort_apply">套用</string>
<string name="sort_cancel">取消</string>
<string name="sort_copy">複製</string>
<string name="sort_close">關閉</string>
<string name="sort_clear">清除</string>
<string name="sort_save">保存</string>
<string name="player_speed">播放速度</string>
<string name="subtitles_settings">字幕設定</string>
<string name="subs_text_color">字體顏色</string>
<string name="subs_outline_color">輪廓顏色</string>
<string name="subs_background_color">背景顏色</string>
<string name="subs_window_color">視窗顏色</string>
<string name="subs_edge_type">邊緣類型</string>
<string name="subs_subtitle_elevation">字幕高度</string>
<string name="subs_font">字體</string>
<string name="subs_font_size">字體大小</string>
<string name="search_provider_text_providers">按片源搜尋</string>
<string name="search_provider_text_types">按類型搜尋</string>
<string name="benene_count_text">送開發者 %d 根香蕉</string>
<string name="benene_count_text_none">不送香蕉</string>
<string name="subs_auto_select_language">自動選擇語言</string>
<string name="subs_download_languages">下載語言</string>
<string name="subs_subtitle_languages">字幕語言</string>
<string name="subs_hold_to_reset_to_default">按住重設為預設值</string>
<string name="subs_import_text" formatted="true">將字體導入到 %s</string>
<string name="continue_watching">繼續觀看</string>
<string name="action_remove_watching">移除</string>
<string name="action_open_watching">更多資訊</string>
<string name="action_open_play">@string/home_play </string>
<string name="vpn_might_be_needed">此片源可能需要 VPN 才能正常使用</string>
<string name="vpn_torrent">此片源是種子,建議使用 VPN</string>
<string name="provider_info_meta">站點不提供元數據,如果站點上不存在元數據,影片載入將失敗。</string>
<string name="torrent_plot">簡介</string>
<string name="normal_no_plot">未找到簡介</string>
<string name="torrent_no_plot">未找到簡介</string>
<string name="show_log_cat">顯示 logcat 🐈</string>
<string name="picture_in_picture">字母畫面</string>
<string name="picture_in_picture_des">在其他應用程式上的子母畫面中繼續播放</string>
<string name="player_size_settings">播放器調整大小按鈕</string>
<string name="player_size_settings_des">移除黑色邊框</string>
<string name="player_subtitles_settings">字幕</string>
<string name="player_subtitles_settings_des">播放器字幕設定</string>
<string name="chromecast_subtitles_settings">Chromecast 字幕</string>
<string name="chromecast_subtitles_settings_des">Chromecast 字幕設定</string>
<string name="eigengraumode_settings">播放速度</string>
<string name="eigengraumode_settings_des">在播放器中添加播放速度選項</string>
<string name="swipe_to_seek_settings">活動控制進度</string>
<string name="swipe_to_seek_settings_des">左右滑動控制播放進度</string>
<string name="swipe_to_change_settings">滑動更改設定</string>
<string name="swipe_to_change_settings_des">上下滑動更改亮度或音量</string>
<string name="autoplay_next_settings">自動播放下一集</string>
<string name="autoplay_next_settings_des">播放完畢後播放下一集</string>
<string name="double_tap_to_seek_settings">輕按兩下以控制進度</string>
<string name="double_tap_to_pause_settings">輕按兩下以暫停</string>
<string name="double_tap_to_seek_amount_settings">輕按兩下以控制進度時間</string>
<string name="double_tap_to_seek_settings_des">在右側或左側輕按兩次以向前或向後快轉
</string>
<string name="double_tap_to_pause_settings_des">輕按兩下中間以暫停</string>
<string name="use_system_brightness_settings">使用系統亮度</string>
<string name="use_system_brightness_settings_des">在應用程序播放器中使用系統亮度替代黑色遮罩</string>
<string name="episode_sync_settings">更新觀看進度</string>
<string name="episode_sync_settings_des">自動同步當前劇集進度</string>
<string name="restore_settings">從備份中恢復資料</string>
<string name="backup_settings">備份資料</string>
<string name="restore_success">已載入備份資料</string>
<string name="restore_failed_format" formatted="true">無法從 %s 檔案中還原資料</string>
<string name="backup_success">成功儲存資料</string>
<string name="backup_failed">缺少儲存權限,請重試</string>
<string name="backup_failed_error_format">備份 %s 錯誤</string>
<string name="search">搜尋</string>
<string name="category_account">帳號</string>
<string name="category_updates">更新與備份</string>
<string name="settings_info">資訊</string>
<string name="advanced_search">進階搜尋</string>
<string name="advanced_search_des">為您提供按片源分開的搜尋結果</string>
<string name="bug_report_settings_off">僅在崩潰時傳送資料</string>
<string name="bug_report_settings_on">不傳送資料</string>
<string name="show_fillers_settings">顯示動畫外傳</string>
<string name="show_trailers_settings">顯示預告片</string>
<string name="kitsu_settings">顯示來自 Kitsu 的封面</string>
<string name="pref_filter_search_quality">在搜尋結果中隱藏選中的影片畫質</string>
<string name="automatic_plugin_updates">自動更新外掛程式</string>
<string name="updates_settings">顯示應用更新</string>
<string name="updates_settings_des">啟動時自動搜尋更新</string>
<string name="uprereleases_settings">更新至預覽版</string>
<string name="uprereleases_settings_des">搜尋預覽版更新而不是僅搜尋正式版</string>
<string name="github">Github</string>
<string name="lightnovel">由相同開發者開發的輕小說應用程式</string>
<string name="anim">由相同開發者開發的動漫應用程式</string>
<string name="discord">加入 Discord</string>
<string name="benene">送開發者一根香蕉</string>
<string name="benene_des">送香蕉</string>
<string name="app_language">應用程式語言</string>
<string name="no_chromecast_support_toast">此片源不支援 Chromecast</string>
<string name="no_links_found_toast">未找到連結</string>
<string name="copy_link_toast">連結已複製到剪貼簿</string>
<string name="play_episode_toast">播放劇集</string>
<string name="subs_default_reset_toast">重設為預設值</string>
<string name="acra_report_toast">很抱歉,應用崩潰了,將傳送一份匿名錯誤報告給開發者</string>
<string name="season"></string>
<string name="season_format">%s %d%s</string>
<string name="no_season">無季</string>
<string name="episode"></string>
<string name="episodes"></string>
<string name="episodes_range">%d-%d</string>
<string name="episode_format" formatted="true">%d %s</string>
<string name="season_short">S</string>
<string name="episode_short">E</string>
<string name="no_episodes_found">未找到劇集</string>
<string name="delete_file">刪除文件</string>
<string name="delete">刪除</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">暫停</string>
<string name="resume">繼續</string>
<string name="go_back_30">-30</string>
<string name="go_forward_30">+30</string>
<string name="delete_message" formatted="true">這將永遠刪除 %s\n你確定嗎?</string>
<string name="resume_time_left" formatted="true">剩餘 %d 分鐘</string>
<string name="status_ongoing">連載中</string>
<string name="status_completed">已完結</string>
<string name="status">狀態</string>
<string name="year">年份</string>
<string name="rating">評分</string>
<string name="duration">時間</string>
<string name="site">網站</string>
<string name="synopsis">簡介</string>
<string name="queued">已加入佇列</string>
<string name="no_subtitles">無字幕</string>
<string name="default_subtitles">預設</string>
<string name="free_storage">空閒</string>
<string name="used_storage">已使用</string>
<string name="app_storage">應用程式</string>
<!--plural-->
<string name="movies">電影</string>
<string name="tv_series">電視劇</string>
<string name="cartoons">卡通</string>
<string name="anime">動漫</string>
<string name="torrent">種子</string>
<string name="documentaries">紀錄片</string>
<string name="ova">原創動畫錄影帶</string>
<string name="asian_drama">亞洲劇</string>
<string name="livestreams">直播</string>
<string name="nsfw">NSFW</string>
<string name="others">其他</string>
<!--singular-->
<string name="movies_singular">電影</string>
<string name="tv_series_singular">電視劇</string>
<string name="cartoons_singular">卡通</string>
<string name="anime_singular">@string/anime</string>
<string name="ova_singular">@string/ova</string>
<string name="torrent_singular">種子</string>
<string name="documentaries_singular">紀錄片</string>
<string name="asian_drama_singular">亞洲劇</string>
<string name="live_singular">直播</string>
<string name="nsfw_singular">NSFW</string>
<string name="other_singular">其他</string>
<string name="source_error">來源錯誤</string>
<string name="remote_error">遠端錯誤</string>
<string name="render_error">渲染器錯誤</string>
<string name="unexpected_error">意料之外的播放器錯誤</string>
<string name="storage_error">下載錯誤,請檢查儲存權限</string>
<string name="episode_action_chromecast_episode">Chromecast 劇集</string>
<string name="episode_action_chromecast_mirror">Chromecast 鏡像</string>
<string name="episode_action_play_in_app">在應用程式中播放</string>
<string name="episode_action_play_in_format">在 %s 中播放</string>
<string name="episode_action_play_in_browser">在瀏覽器中播放</string>
<string name="episode_action_copy_link">複製連結</string>
<string name="episode_action_auto_download">自動下載</string>
<string name="episode_action_download_mirror">下載鏡像</string>
<string name="episode_action_reload_links">重新載入連結</string>
<string name="episode_action_download_subtitle">下載字幕</string>
<string name="show_hd">畫質標籤</string>
<string name="show_dub">配音標籤</string>
<string name="show_sub">字幕標籤</string>
<string name="show_title">標題</string>
<string name="show_hd_key" translatable="false">show_hd_key</string>
<string name="show_dub_key" translatable="false">show_dub_key</string>
<string name="show_sub_key" translatable="false">show_sub_key</string>
<string name="show_title_key" translatable="false">show_title_key</string>
<string name="poster_ui_settings">封面內容</string>
<string name="no_update_found">未找到更新</string>
<string name="check_for_update">检查更新</string>
<string name="video_lock">鎖定</string>
<string name="video_aspect_ratio_resize">調整大小</string>
<string name="video_source">來源</string>
<string name="video_skip_op">跳過片頭</string>
<string name="dont_show_again">不再顯示</string>
<string name="skip_update">跳過此更新</string>
<string name="update">更新</string>
<string name="watch_quality_pref">偏好播放畫質</string>
<string name="limit_title">影片播放器標題最大字數</string>
<string name="limit_title_rez">影片播放器標題</string>
<string name="video_buffer_size_settings">影片緩衝大小</string>
<string name="video_buffer_length_settings">影片緩衝長度</string>
<string name="video_buffer_disk_settings">影片快取存儲</string>
<string name="video_buffer_clear_settings">清除影片和圖片快取</string>
<string name="video_ram_description">如果設定得太高會導致隨機崩潰。 如果您的記憶體不足(例如 Android TV 或舊手機),請不要更改</string>
<string name="video_disk_description">如果您將其設定得太高,可能會導致儲存空間不足的系統(例如 Android TV 設備)出現問題</string>
<string name="dns_pref">DNS over HTTPS</string>
<string name="dns_pref_summary">用於繞過網路服務供應商的封鎖</string>
<string name="add_site_pref">複製片源</string>
<string name="remove_site_pref">移除片源</string>
<string name="add_site_summary">添加具有不同URL的現有站點複製</string>
<string name="download_path_pref">下載路徑</string>
<string name="nginx_url_pref">Nginx 伺服器連結</string>
<string name="display_subbed_dubbed_settings">顯示有配音/字幕的動漫</string>
<string name="resize_fit">適應螢幕</string>
<string name="resize_fill">拉伸</string>
<string name="resize_zoom">縮放</string>
<string name="legal_notice">免責聲明</string>
<string name="legal_notice_key" translatable="false">legal_notice_key</string>
<string name="legal_notice_text" translatable="false">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.
</string>
<string name="category_general">通用</string>
<string name="random_button_settings">隨機按鈕</string>
<string name="random_button_settings_desc">在主頁中顯示隨機按鈕</string>
<string name="provider_lang_settings">片源語言</string>
<string name="app_layout">應用佈局</string>
<string name="preferred_media_settings">偏好類型</string>
<string name="enable_nsfw_on_providers">在支援的片源中啟用 NSFW 內容</string>
<string name="subtitles_encoding">字幕編碼</string>
<string name="category_providers">片源</string>
<string name="category_ui">佈局</string>
<string name="automatic">自動</string>
<string name="tv_layout">電視佈局</string>
<string name="phone_layout">手機佈局</string>
<string name="emulator_layout">模擬器佈局</string>
<string name="primary_color_settings">主題色</string>
<string name="app_theme_settings">應用程式主題</string>
<string name="bottom_title_settings">封面標題位置</string>
<string name="bottom_title_settings_des">將標題移到封面下方</string>
<!-- account stuff -->
<string name="anilist_key" translatable="false">anilist_key</string>
<string name="mal_key" translatable="false">mal_key</string>
<string name="opensubtitles_key" translatable="false">opensubtitles_key</string>
<string name="nginx_key" translatable="false">nginx_key</string>
<string name="example_password">密碼</string>
<string name="example_username">用戶名</string>
<string name="example_email">電子郵件</string>
<string name="example_ip">IP</string>
<string name="example_site_name">網站名稱</string>
<string name="example_site_url">網站連結</string>
<string name="example_lang_name">語言代號 (zh_TW)</string>
<!--
<string name="mal_account_settings" translatable="false">MAL</string>
<string name="anilist_account_settings" translatable="false">AniList</string>
<string name="tmdb_account_settings" translatable="false">TMDB</string>
<string name="imdb_account_settings" translatable="false">IMDB</string>
<string name="kitsu_account_settings" translatable="false">Kitsu</string>
<string name="trakt_account_settings" translatable="false">Trakt</string>
-->
<string name="login_format" formatted="true">%s %s</string>
<string name="account">帳號</string>
<string name="logout">登出</string>
<string name="login">登入</string>
<string name="switch_account">切換帳號</string>
<string name="add_account">添加帳號</string>
<string name="create_account">創建帳號</string>
<string name="add_sync">添加同步</string>
<string name="added_sync_format" formatted="true">已添加 %s</string>
<string name="upload_sync">同步</string>
<string name="sync_score">評分</string>
<string name="sync_score_format" formatted="true">%d / 10</string>
<string name="sync_total_episodes_none">/??</string>
<string name="sync_total_episodes_some" formatted="true">/%d</string>
<string name="authenticated_user" formatted="true">已驗證 %s</string>
<string name="authenticated_user_fail" formatted="true">驗證 %s 失敗</string>
<!-- ============ -->
<string name="none"></string>
<string name="normal">普通</string>
<string name="all">全部</string>
<string name="max">最大</string>
<string name="min">最小</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">輪廓</string>
<string name="subtitles_depressed">凹陷</string>
<string name="subtitles_shadow">陰影</string>
<string name="subtitles_raised">凸出</string>
<string name="subtitle_offset">同步字幕</string>
<string name="subtitle_offset_hint">1000ms</string>
<string name="subtitle_offset_title">字幕延遲</string>
<string name="subtitle_offset_extra_hint_later_format">如果字幕過早顯示 %dms ,請使用此選項</string>
<string name="subtitle_offset_extra_hint_before_format">如果字幕過晚顯示 %dms ,請使用此選項</string>
<string name="subtitle_offset_extra_hint_none_format">無字幕延遲</string>
<!--
Example text (pangram) can optionally be translated; if you do, include all the letters in the alphabet,
see:
https://en.wikipedia.org/w/index.php?title=Pangram&oldid=225849300
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
-->
<string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string>
<string name="recommended">推薦</string>
<string name="player_loaded_subtitles" formatted="true">已載入 %s</string>
<string name="player_load_subtitles">從檔案載入</string>
<string name="player_load_subtitles_online">從網路載入</string>
<string name="downloaded_file">下載的檔案</string>
<string name="actor_main">主角</string>
<string name="actor_supporting">配角</string>
<string name="actor_background">群演</string>
<string name="home_source">來源</string>
<string name="home_random">隨機</string>
<string name="coming_soon">即將到來…</string>
<string name="quality_cam">Cam</string>
<string name="quality_cam_rip">Cam</string>
<string name="quality_cam_hd">Cam</string>
<string name="quality_hq">HQ</string>
<string name="quality_hd">HD</string>
<string name="quality_ts">TS</string>
<string name="quality_tc">TC</string>
<string name="quality_blueray">BlueRay</string>
<string name="quality_workprint">WP</string>
<string name="quality_dvd">DVD</string>
<string name="quality_4k">4K</string>
<string name="quality_sd">SD</string>
<string name="quality_uhd">UHD</string>
<string name="quality_hdr">HDR</string>
<string name="quality_sdr">SDR</string>
<string name="quality_webrip">Web</string>
<string name="poster_image">封面圖片</string>
<string name="category_player">播放器</string>
<string name="resolution_and_title">解析度與標題</string>
<string name="title">標題</string>
<string name="resolution">解析度</string>
<string name="error_invalid_id">無效 ID</string>
<string name="error_invalid_data">無效資料</string>
<string name="error_invalid_url">無效連結</string>
<string name="error">錯誤</string>
<string name="subtitles_remove_captions">移除隱藏式字幕</string>
<string name="subtitles_remove_bloat">移除字幕廣告</string>
<string name="subtitles_filter_lang">按偏好片源語言過濾</string>
<string name="extras">附加</string>
<string name="trailer">預告片</string>
<string name="network_adress_example">播放連結</string>
<string name="referer">推薦</string>
<string name="next">下一個</string>
<string name="provider_languages_tip">觀看這些語言的影片</string>
<string name="previous">上一個</string>
<string name="skip_setup">跳過設定</string>
<string name="app_layout_subtext">更改應用程式的外觀以適應你的設備</string>
<string name="crash_reporting_title">崩潰報告</string>
<string name="preferred_media_subtext">你想要看什麼</string>
<string name="setup_done">完成</string>
<string name="extensions">擴充功能</string>
<string name="add_repository">添加資源庫</string>
<string name="repository_name_hint">資源庫名稱</string>
<string name="repository_url_hint">資源庫連結</string>
<string name="plugin_loaded">外掛程式已載入</string>
<string name="plugin_deleted">外掛程式已刪除</string>
<string name="plugin_load_fail" formatted="true">載入 %s 失敗</string>
<string name="is_adult">18+</string>
<string name="batch_download_start_format" formatted="true">開始下載 %d %s</string>
<string name="batch_download_finish_format" formatted="true">下載 %d %s 成功</string>
<string name="batch_download_nothing_to_download_format" formatted="true">全部 %s 已經下載</string>
<string name="batch_download">批次下載</string>
<string name="plugin_singular">外掛程式</string>
<string name="plugin">外掛程式</string>
<string name="delete_repository_plugins">這也將刪除所有資源庫外掛程式</string>
<string name="delete_repository">刪除資源庫</string>
<string name="setup_extensions_subtext">下載你所需的片源</string>
<string name="plugins_downloaded" formatted="true">已下載:%d</string>
<string name="plugins_disabled" formatted="true">已禁用:%d</string>
<string name="plugins_not_downloaded" formatted="true">未下載:%d</string>
<string name="plugins_updated" formatted="true">已更新 %d 外掛程式</string>
<string name="blank_repo_message">CloudStream 預設沒有安裝任何片源。您需要從資源庫安裝站點。\n\n由於 Sky Uk Limited 的無腦 DMCA 刪除🤮,我們無法在應用程式中連結資源庫站點。\n\n加入我們的 Discord 獲得連結或自己在網路上搜尋</string>
<string name="view_public_repositories_button">查看</string>
<string name="view_public_repositories_button_short">公開列表</string>
<string name="uppercase_all_subtitles">字幕全大寫</string>
<string name="download_all_plugins_from_repo">從此資源庫下載所有外掛程式?</string>
<string name="single_plugin_disabled" formatted="true">%s (禁用)</string>
<string name="tracks">軌道</string>
<string name="audio_tracks">音頻軌道</string>
<string name="video_tracks">影片軌道</string>
<string name="apply_on_restart">重新啟動時生效</string>
<string name="safe_mode_title">安全模式已啟用</string>
<string name="safe_mode_description">發生了不可恢復的崩潰,我們已自動禁用所有外掛程式,因此您可以找到並刪除導致問題的應用程式。</string>
<string name="safe_mode_crash_info">查看崩潰資訊</string>
<string name="extension_rating" formatted="true">評分:%s</string>
<string name="extension_description">簡介</string>
<string name="extension_version">版本</string>
<string name="extension_status">狀態</string>
<string name="extension_size">大小</string>
<string name="extension_authors">作者</string>
<string name="extension_types">類型</string>
<string name="extension_language">語言</string>
<string name="extension_install_first">請先安裝外掛程式</string>
<string name="hls_playlist">HLS 播放清單</string>
<string name="player_pref">偏好影片播放器</string>
<string name="player_settings_play_in_app">內部播放器</string>
<string name="player_settings_play_in_vlc">VLC</string>
<string name="player_settings_play_in_mpv">MPV</string>
<string name="player_settings_play_in_web">網路影片播放</string>
<string name="player_settings_play_in_browser">瀏覽器</string>
<string name="app_not_found_error">未找到應用</string>
<string name="all_languages_preference">所有語言</string>
<string name="skip_type_format" formatted="true">跳過 %s</string>
<string name="skip_type_op">片頭</string>
<string name="skip_type_ed">片尾</string>
<string name="skip_type_recap">前情回顧</string>
<string name="skip_type_mixed_ed">混合片尾</string>
<string name="skip_type_mixed_op">混合片頭</string>
<string name="skip_type_creddits">致謝名單</string>
<string name="skip_type_intro">介紹</string>
<string name="clear_history">清除歷史紀錄</string>
<string name="history">歷史紀錄</string>
</resources>

View file

@ -209,6 +209,7 @@
<string name="pref_filter_search_quality">在搜索结果中隐藏选中视频画质</string>
<string name="automatic_plugin_updates">自动更新插件</string>
<string name="automatic_plugin_download">自动下载插件</string>
<string name="updates_settings">显示应用更新</string>
<string name="updates_settings_des">启动时自动搜索更新</string>
<string name="uprereleases_settings">更新至预览版</string>

View file

@ -6,6 +6,7 @@
<string name="search_types_list_key" translatable="false">search_type_list</string>
<string name="auto_update_key" translatable="false">auto_update</string>
<string name="auto_update_plugins_key" translatable="false">auto_update_plugins</string>
<string name="auto_download_plugins_key" translatable="false">auto_download_plugins_key</string>
<string name="skip_update_key" translatable="false">skip_update_key</string>
<string name="prerelease_update_key" translatable="false">prerelease_update</string>
<string name="manual_check_update_key" translatable="false">manual_check_update</string>
@ -269,6 +270,7 @@
<string name="pref_filter_search_quality">Hide selected video quality on Search results</string>
<string name="automatic_plugin_updates">Automatic plugin updates</string>
<string name="automatic_plugin_download">Automatically download plugins</string>
<string name="updates_settings">Show app updates</string>
<string name="updates_settings_des">Automatically search for new updates on start</string>
<string name="uprereleases_settings">Update to prereleases</string>

View file

@ -33,6 +33,11 @@
android:icon="@drawable/ic_baseline_extension_24"
android:key="@string/auto_update_plugins_key"
android:title="@string/automatic_plugin_updates" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_baseline_extension_24"
android:key="@string/auto_download_plugins_key"
android:title="@string/automatic_plugin_download" />
<SwitchPreference
android:icon="@drawable/ic_baseline_notifications_active_24"
android:summary="@string/updates_settings_des"