Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt
This commit is contained in:
Antony 2022-08-22 00:29:31 +02:00
commit af35ddf598
39 changed files with 351 additions and 308 deletions

View file

@ -41,6 +41,7 @@ import com.lagradost.cloudstream3.CommonActivity.updateLocale
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.plugins.PluginManager
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
@ -78,6 +79,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.nicehttp.Requests
import com.lagradost.nicehttp.ResponseParser
import kotlinx.android.synthetic.main.activity_main.*
@ -130,6 +132,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
companion object {
const val TAG = "MAINACT"
val afterPluginsLoadedEvent = Event<Boolean>()
val mainPluginsLoadedEvent =
Event<Boolean>() // homepage api, used to speed up time to load for homepage
val afterRepositoryLoadedEvent = Event<Boolean>()
}
@ -436,6 +440,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
changeStatusBarState(isEmulatorSettings())
ioSafe {
getKey<String>(USER_SELECTED_HOMEPAGE_API)?.let { homeApi ->
mainPluginsLoadedEvent.invoke(loadSinglePlugin(this@MainActivity, homeApi))
} ?: run {
mainPluginsLoadedEvent.invoke(false)
}
if (settingsManager.getBoolean(getString(R.string.auto_update_plugins_key), true)) {
PluginManager.updateAllOnlinePluginsAndLoadThem(this@MainActivity)
} else {

View file

@ -0,0 +1,102 @@
package com.lagradost.cloudstream3.network
import android.util.Log
import android.webkit.CookieManager
import androidx.annotation.AnyThread
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.debugWarning
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.cookies
import kotlinx.coroutines.runBlocking
import okhttp3.*
@AnyThread
class CloudflareKiller : Interceptor {
companion object {
const val TAG = "CloudflareKiller"
fun parseCookieMap(cookie: String): Map<String, String> {
return cookie.split(";").associate {
val split = it.split("=")
(split.getOrNull(0)?.trim() ?: "") to (split.getOrNull(1)?.trim() ?: "")
}.filter { it.key.isNotBlank() && it.value.isNotBlank() }
}
}
val savedCookies: MutableMap<String, Map<String, String>> = mutableMapOf()
override fun intercept(chain: Interceptor.Chain): Response = runBlocking {
val request = chain.request()
val cookies = savedCookies[request.url.host]
if (cookies == null) {
bypassCloudflare(request)?.let {
Log.d(TAG, "Succeeded bypassing cloudflare: ${request.url}")
return@runBlocking it
}
} else {
return@runBlocking proceed(request, cookies)
}
debugWarning({ true }) { "Failed cloudflare at: ${request.url}" }
return@runBlocking chain.proceed(request)
}
private fun getWebViewCookie(url: String): String? {
return CookieManager.getInstance()?.getCookie(url)
}
/**
* Returns true if the cf cookies were successfully fetched from the CookieManager
* Also saves the cookies.
* */
private fun trySolveWithSavedCookies(request: Request): Boolean {
// Not sure if this takes expiration into account
return getWebViewCookie(request.url.toString())?.let { cookie ->
cookie.contains("cf_clearance").also { solved ->
if (solved) savedCookies[request.url.host] = parseCookieMap(cookie)
}
} ?: false
}
private suspend fun proceed(request: Request, cookies: Map<String, String>): Response {
val userAgentMap = WebViewResolver.getWebViewUserAgent()?.let {
mapOf("user-agent" to it)
} ?: emptyMap()
val headers =
getHeaders(request.headers.toMap() + userAgentMap, cookies + request.cookies)
return app.baseClient.newCall(
request.newBuilder()
.headers(headers)
.build()
).await()
}
private suspend fun bypassCloudflare(request: Request): Response? {
val url = request.url.toString()
// If no cookies then try to get them
// Remove this if statement if cookies expire
if (!trySolveWithSavedCookies(request)) {
Log.d(TAG, "Loading webview to solve cloudflare for ${request.url}")
WebViewResolver(
// Never exit based on url
Regex(".^"),
// Cloudflare needs default user agent
userAgent = null,
// Cannot use okhttp (i think intercepting cookies fails which causes the issues)
useOkhttp = false,
// Match every url for the requestCallBack
additionalUrls = listOf(Regex("."))
).resolveUsingWebView(
url
) {
trySolveWithSavedCookies(request)
}
}
val cookies = savedCookies[request.url.host] ?: return null
return proceed(request, cookies)
}
}

View file

@ -1,13 +1,10 @@
package com.lagradost.cloudstream3.network
import androidx.annotation.AnyThread
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app
import com.lagradost.nicehttp.Requests.Companion.await
import com.lagradost.nicehttp.getCookies
import com.lagradost.nicehttp.cookies
import kotlinx.coroutines.runBlocking
import okhttp3.Headers
import okhttp3.Headers.Companion.toHeaders
import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response

View file

@ -43,10 +43,10 @@ fun Requests.initClient(context: Context): OkHttpClient {
return baseClient
}
val Request.cookies: Map<String, String>
get() {
return this.headers.getCookies("Cookie")
}
//val Request.cookies: Map<String, String>
// get() {
// return this.headers.getCookies("Cookie")
// }
private val DEFAULT_HEADERS = mapOf("user-agent" to USER_AGENT)

View file

@ -4,10 +4,12 @@ import android.annotation.SuppressLint
import android.net.http.SslError
import android.webkit.*
import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.utils.Coroutines.main
import com.lagradost.cloudstream3.utils.Coroutines.mainWork
import com.lagradost.nicehttp.requestCreator
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
@ -15,16 +17,39 @@ import okhttp3.Interceptor
import okhttp3.Request
import okhttp3.Response
import java.net.URI
import java.util.concurrent.TimeUnit
/**
* When used as Interceptor additionalUrls cannot be returned, use WebViewResolver(...).resolveUsingWebView(...)
* @param interceptUrl will stop the WebView when reaching this url.
* @param additionalUrls this will make resolveUsingWebView also return all other requests matching the list of Regex.
* @param userAgent if null then will use the default user agent
* @param useOkhttp will try to use the okhttp client as much as possible, but this might cause some requests to fail. Disable for cloudflare.
* */
class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> = emptyList()) :
class WebViewResolver(
val interceptUrl: Regex,
val additionalUrls: List<Regex> = emptyList(),
val userAgent: String? = USER_AGENT,
val useOkhttp: Boolean = true
) :
Interceptor {
companion object {
var webViewUserAgent: String? = null
@JvmName("getWebViewUserAgent1")
fun getWebViewUserAgent(): String? {
return webViewUserAgent ?: context?.let { ctx ->
runBlocking {
mainWork {
WebView(ctx).settings.userAgentString.also { userAgent ->
webViewUserAgent = userAgent
}
}
}
}
}
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
return runBlocking {
@ -38,7 +63,7 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
referer: String? = null,
method: String = "GET",
requestCallBack: (Request) -> Boolean = { false },
) : Pair<Request?, List<Request>> {
): Pair<Request?, List<Request>> {
return resolveUsingWebView(
requestCreator(method, url, referer = referer), requestCallBack
)
@ -57,12 +82,15 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
val headers = request.headers
println("Initial web-view request: $url")
var webView: WebView? = null
// Extra assurance it exits as it should.
var shouldExit = false
fun destroyWebView() {
main {
webView?.stopLoading()
webView?.destroy()
webView = null
shouldExit = true
println("Destroyed webview")
}
}
@ -72,7 +100,7 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
main {
// Useful for debugging
// WebView.setWebContentsDebuggingEnabled(true)
WebView.setWebContentsDebuggingEnabled(true)
try {
webView = WebView(
AcraApplication.context
@ -81,9 +109,14 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
// Bare minimum to bypass captcha
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.userAgentString = USER_AGENT
webViewUserAgent = settings.userAgentString
// Don't set user agent, setting user agent will make cloudflare break.
if (userAgent != null) {
settings.userAgentString = userAgent
}
// Blocks unnecessary images, remove if captcha fucks.
settings.blockNetworkImage = true
// settings.blockNetworkImage = true
}
webView?.webViewClient = object : WebViewClient() {
@ -92,11 +125,11 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
request: WebResourceRequest
): WebResourceResponse? = runBlocking {
val webViewUrl = request.url.toString()
// println("Loading WebView URL: $webViewUrl")
println("Loading WebView URL: $webViewUrl")
if (interceptUrl.containsMatchIn(webViewUrl)) {
fixedRequest = request.toRequest().also {
if (requestCallBack(it)) destroyWebView()
requestCallBack(it)
}
println("Web-view request finished: $webViewUrl")
destroyWebView()
@ -158,22 +191,22 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
null,
null
)
webViewUrl.contains("recaptcha") -> super.shouldInterceptRequest(
webViewUrl.contains("recaptcha") || webViewUrl.contains("/cdn-cgi/") -> super.shouldInterceptRequest(
view,
request
)
request.method == "GET" -> app.get(
useOkhttp && request.method == "GET" -> app.get(
webViewUrl,
headers = request.requestHeaders
).okhttpResponse.toWebResourceResponse()
request.method == "POST" -> app.post(
useOkhttp && request.method == "POST" -> app.post(
webViewUrl,
headers = request.requestHeaders
).okhttpResponse.toWebResourceResponse()
else -> return@runBlocking super.shouldInterceptRequest(
else -> super.shouldInterceptRequest(
view,
request
)
@ -204,7 +237,7 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
val delayTime = 100L
// A bit sloppy, but couldn't find a better way
while (loop < totalTime / delayTime) {
while (loop < totalTime / delayTime && !shouldExit) {
if (fixedRequest != null) return fixedRequest to extraRequestList
delay(delayTime)
loop += 1
@ -212,30 +245,31 @@ class WebViewResolver(val interceptUrl: Regex, val additionalUrls: List<Regex> =
println("Web-view timeout after ${totalTime / 1000}s")
destroyWebView()
return null to extraRequestList
return fixedRequest to extraRequestList
}
fun WebResourceRequest.toRequest(): Request {
val webViewUrl = this.url.toString()
}
return requestCreator(
this.method,
webViewUrl,
this.requestHeaders,
)
}
fun WebResourceRequest.toRequest(): Request {
val webViewUrl = this.url.toString()
fun Response.toWebResourceResponse(): WebResourceResponse {
val contentTypeValue = this.header("Content-Type")
// 1. contentType. 2. charset
val typeRegex = Regex("""(.*);(?:.*charset=(.*)(?:|;)|)""")
return if (contentTypeValue != null) {
val found = typeRegex.find(contentTypeValue)
val contentType = found?.groupValues?.getOrNull(1)?.ifBlank { null } ?: contentTypeValue
val charset = found?.groupValues?.getOrNull(2)?.ifBlank { null }
WebResourceResponse(contentType, charset, this.body?.byteStream())
} else {
WebResourceResponse("application/octet-stream", null, this.body?.byteStream())
}
return requestCreator(
this.method,
webViewUrl,
this.requestHeaders,
)
}
fun Response.toWebResourceResponse(): WebResourceResponse {
val contentTypeValue = this.header("Content-Type")
// 1. contentType. 2. charset
val typeRegex = Regex("""(.*);(?:.*charset=(.*)(?:|;)|)""")
return if (contentTypeValue != null) {
val found = typeRegex.find(contentTypeValue)
val contentType = found?.groupValues?.getOrNull(1)?.ifBlank { null } ?: contentTypeValue
val charset = found?.groupValues?.getOrNull(2)?.ifBlank { null }
WebResourceResponse(contentType, charset, this.body.byteStream())
} else {
WebResourceResponse("application/octet-stream", null, this.body.byteStream())
}
}

View file

@ -125,7 +125,7 @@ object PluginManager {
Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/plugins"
// Maps filepath to plugin
private val plugins: MutableMap<String, Plugin> =
val plugins: MutableMap<String, Plugin> =
LinkedHashMap<String, Plugin>()
// Maps urls to plugin
@ -164,6 +164,18 @@ object PluginManager {
var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean {
return (getPluginsOnline().firstOrNull { it.internalName == apiName }
?: getPluginsLocal().firstOrNull { it.internalName == apiName })?.let { savedData ->
// OnlinePluginData(savedData, onlineData)
loadPlugin(
activity,
File(savedData.filePath),
savedData
)
} ?: false
}
/**
* Needs to be run before other plugin loading because plugin loading can not be overwritten
* 1. Gets all online data about the downloaded plugins
@ -376,7 +388,7 @@ object PluginManager {
file ?: return false,
PluginData(internalName, pluginUrl, true, file.absolutePath, PLUGIN_VERSION_NOT_SET)
)
} catch (e : Exception) {
} catch (e: Exception) {
logError(e)
return false
}

View file

@ -1,16 +1,16 @@
package com.lagradost.cloudstream3.ui
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.USER_AGENT
import com.lagradost.cloudstream3.network.WebViewResolver
import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringRepo
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
import kotlinx.android.synthetic.main.fragment_webview.*
@ -48,7 +48,9 @@ class WebviewFragment : Fragment() {
}
web_view.settings.javaScriptEnabled = true
web_view.settings.domStorageEnabled = true
web_view.settings.userAgentString = USER_AGENT
WebViewResolver.webViewUserAgent = web_view.settings.userAgentString
// web_view.settings.userAgentString = USER_AGENT
web_view.loadUrl(url)
}

View file

@ -37,6 +37,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadHelper
import com.lagradost.cloudstream3.utils.VideoDownloadManager
import kotlinx.android.synthetic.main.fragment_downloads.*
import kotlinx.android.synthetic.main.stream_input.*
import android.text.format.Formatter.formatShortFileSize
const val DOWNLOAD_NAVIGATE_TO = "downloadpage"
@ -44,10 +45,6 @@ const val DOWNLOAD_NAVIGATE_TO = "downloadpage"
class DownloadFragment : Fragment() {
private lateinit var downloadsViewModel: DownloadViewModel
private fun getBytesAsText(bytes: Long): String {
return "%.1f".format(bytes / 1000000000f)
}
private fun View.setLayoutWidth(weight: Long) {
val param = LinearLayout.LayoutParams(
0,
@ -101,7 +98,7 @@ class DownloadFragment : Fragment() {
download_free_txt?.text =
getString(R.string.storage_size_format).format(
getString(R.string.free_storage),
getBytesAsText(it)
formatShortFileSize(view.context, it)
)
download_free?.setLayoutWidth(it)
}
@ -109,7 +106,7 @@ class DownloadFragment : Fragment() {
download_used_txt?.text =
getString(R.string.storage_size_format).format(
getString(R.string.used_storage),
getBytesAsText(it)
formatShortFileSize(view.context, it)
)
download_used?.setLayoutWidth(it)
download_storage_appbar?.isVisible = it > 0
@ -118,7 +115,7 @@ class DownloadFragment : Fragment() {
download_app_txt?.text =
getString(R.string.storage_size_format).format(
getString(R.string.app_storage),
getBytesAsText(it)
formatShortFileSize(view.context, it)
)
download_app?.setLayoutWidth(it)
}

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.download
import android.annotation.SuppressLint
import android.text.format.Formatter.formatShortFileSize
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -113,7 +114,7 @@ class DownloadHeaderAdapter(
}
title.text = d.name
val mbString = "%.1f".format(card.totalBytes / 1000000f)
val mbString = formatShortFileSize(itemView.context, card.totalBytes)
//val isMovie = d.type.isMovieType()
if (card.child != null) {

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.download
import android.animation.ObjectAnimator
import android.text.format.Formatter.formatShortFileSize
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
@ -171,8 +172,8 @@ class EasyDownloadButton : IDisposable {
}
textView?.visibility = View.VISIBLE
progressBar.visibility = View.VISIBLE
val currentMbString = "%.1f".format(setCurrentBytes / 1000000f)
val totalMbString = "%.1f".format(setTotalBytes / 1000000f)
val currentMbString = formatShortFileSize(textView?.context, setCurrentBytes)
val totalMbString = formatShortFileSize(textView?.context, setTotalBytes)
textView?.text =
if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else

View file

@ -31,8 +31,8 @@ import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent
import com.lagradost.cloudstream3.mvvm.Resource
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
@ -60,7 +60,6 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.removeLastWatched
import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultWatchState
import com.lagradost.cloudstream3.utils.Event
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@ -70,6 +69,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.setImageBlur
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.widget.CenterZoomLayoutManager
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.fragment_home.home_api_fab
@ -437,11 +437,13 @@ class HomeFragment : Fragment() {
override fun onResume() {
super.onResume()
reloadStored()
afterPluginsLoadedEvent += ::loadHomePage
afterPluginsLoadedEvent += ::firstLoadHomePage
mainPluginsLoadedEvent += ::firstLoadHomePage
}
override fun onStop() {
afterPluginsLoadedEvent -= ::loadHomePage
afterPluginsLoadedEvent -= ::firstLoadHomePage
mainPluginsLoadedEvent -= ::firstLoadHomePage
super.onStop()
}
@ -454,6 +456,14 @@ class HomeFragment : Fragment() {
homeViewModel.loadStoredData(list)
}
private var hasBeenConsumed = false
private fun firstLoadHomePage(successful: Boolean = false) {
// dirty hack to make it only load once
if(hasBeenConsumed) return
hasBeenConsumed = true
loadHomePage(successful)
}
private fun loadHomePage(successful: Boolean = false) {
val apiName = context?.getKey<String>(USER_SELECTED_HOMEPAGE_API)
@ -962,13 +972,13 @@ class HomeFragment : Fragment() {
reloadStored()
loadHomePage()
home_loaded.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { view, _, scrollY, _, oldScrollY ->
home_loaded.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { v, _, scrollY, _, oldScrollY ->
val dy = scrollY - oldScrollY
if (dy > 0) { //check for scroll down
home_api_fab?.shrink() // hide
home_random?.shrink()
} else if (dy < -5) {
if (view?.context?.isTvSettings() == false) {
if (v.context?.isTvSettings() == false) {
home_api_fab?.extend() // show
home_random?.extend()
}

View file

@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI
import com.lagradost.cloudstream3.syncproviders.OAuth2API
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
@ -209,6 +210,7 @@ class SettingsAccount : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_account)
setPaddingBottom()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -21,6 +21,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.navigate
import com.lagradost.cloudstream3.utils.UIHelper.setImage
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import kotlinx.android.synthetic.main.main_settings.*
import kotlinx.android.synthetic.main.standard_toolbar.*
import java.io.File
@ -40,6 +41,15 @@ class SettingsFragment : Fragment() {
}
}
/**
* On TV you cannot properly scroll to the bottom of settings, this fixes that.
* */
fun PreferenceFragmentCompat.setPaddingBottom() {
if (this.context?.isTvSettings() == true) {
listView?.setPadding(0, 0, 0, 100.toPx)
}
}
fun Fragment?.setUpToolbar(title: String) {
if (this == null) return
settings_toolbar?.apply {

View file

@ -25,6 +25,7 @@ import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.ui.EasterEggMonke
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
@ -42,6 +43,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_general)
setPaddingBottom()
}
data class CustomSite(

View file

@ -12,6 +12,7 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
@ -61,6 +62,7 @@ class SettingsLang : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_preferred_media_and_lang)
setPaddingBottom()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -1,6 +1,7 @@
package com.lagradost.cloudstream3.ui.settings
import android.os.Bundle
import android.text.format.Formatter.formatShortFileSize
import android.view.View
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
@ -8,6 +9,7 @@ import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment
import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment
@ -20,6 +22,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_player)
setPaddingBottom()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()
@ -164,8 +167,7 @@ class SettingsPlayer : PreferenceFragmentCompat() {
fun updateSummery() {
try {
pref.summary =
getString(R.string.mb_format).format(getFolderSize(cacheDir) / (1024L * 1024L))
pref.summary = formatShortFileSize(view?.context, getFolderSize(cacheDir))
} catch (e: Exception) {
logError(e)
}

View file

@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.search.SearchResultBuilder
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
@ -19,6 +20,7 @@ class SettingsUI : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_ui)
setPaddingBottom()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
hideKeyboard()

View file

@ -13,6 +13,7 @@ import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.BackupUtils.backup
import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt
@ -31,6 +32,7 @@ class SettingsUpdates : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpToolbar(R.string.category_updates)
setPaddingBottom()
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -134,7 +134,8 @@ class PluginAdapter(
//}
if (data.isDownloaded) {
val plugin = PluginManager.urlPlugins[metadata.url]
// On local plugins page the filepath is provided instead of url.
val plugin = PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url]
if (plugin?.openSettings != null) {
itemView.action_settings?.isVisible = true
itemView.action_settings.setOnClickListener {

View file

@ -6,10 +6,22 @@ import android.widget.SearchView
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.map
import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.observe
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.getPairList
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.ui.settings.appLanguages
import com.lagradost.cloudstream3.ui.settings.getCurrentLocale
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
import com.lagradost.cloudstream3.utils.SubtitleHelper
import com.lagradost.cloudstream3.utils.UIHelper.toPx
import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API
import kotlinx.android.synthetic.main.fragment_plugins.*
const val PLUGINS_BUNDLE_NAME = "name"
@ -32,6 +44,7 @@ class PluginsFragment : Fragment() {
// Since the ViewModel is getting reused the tvTypes must be cleared between uses
pluginViewModel.tvTypes.clear()
pluginViewModel.languages = listOf()
pluginViewModel.search(null)
val name = arguments?.getString(PLUGINS_BUNDLE_NAME)
@ -50,6 +63,27 @@ class PluginsFragment : Fragment() {
R.id.download_all -> {
PluginsViewModel.downloadAll(activity, url, pluginViewModel)
}
R.id.lang_filter -> {
val tempLangs = appLanguages.toMutableList()
val languageCodes = mutableListOf("none") + tempLangs.map { (_, _, iso) -> iso }
val languageNames =
mutableListOf(getString(R.string.no_data)) + tempLangs.map { (emoji, name, iso) ->
val flag =
emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" }
"$flag $name"
}
val selectedList =
pluginViewModel.languages.map { it -> languageCodes.indexOf(it) }
activity?.showMultiDialog(
languageNames,
selectedList,
getString(R.string.provider_lang_settings),
{}) { newList ->
pluginViewModel.languages = newList.map { it -> languageCodes[it] }
pluginViewModel.updateFilteredPlugins()
}
}
else -> {}
}
return@setOnMenuItemClickListener true
@ -94,6 +128,11 @@ class PluginsFragment : Fragment() {
pluginViewModel.handlePluginAction(activity, url, it, isLocal)
}
if (context?.isTvSettings() == true) {
// Scrolling down does not reveal the whole RecyclerView on TV, add to bypass that.
plugin_recycler_view?.setPadding(0, 0, 0, 200.toPx)
}
observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) ->
(plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list)
@ -104,6 +143,7 @@ class PluginsFragment : Fragment() {
if (isLocal) {
// No download button and no categories on local
settings_toolbar?.menu?.findItem(R.id.download_all)?.isVisible = false
settings_toolbar?.menu?.findItem(R.id.lang_filter)?.isVisible = false
pluginViewModel.updatePluginListLocal()
tv_types_scroll_view?.isVisible = false
} else {
@ -123,11 +163,14 @@ class PluginsFragment : Fragment() {
home_select_others
)
// val supportedTypes: Array<String> =
// pluginViewModel.filteredPlugins.value!!.second.flatMap { it -> it.plugin.second.tvTypes ?: listOf("Other") }.distinct().toTypedArray()
// Copy pasted code
for ((button, validTypes) in pairList) {
val validTypesMapped = validTypes.map { it.name }
val isValid =
true //validAPIs.any { api -> validTypes.any { api.supportedTypes.contains(it) } }
val isValid = true
//validTypes.any { it -> supportedTypes.contains(it.name) }
button?.isVisible = isValid
if (isValid) {
fun buttonContains(): Boolean {

View file

@ -38,6 +38,7 @@ class PluginsViewModel : ViewModel() {
var filteredPlugins: LiveData<PluginViewDataUpdate> = _filteredPlugins
val tvTypes = mutableListOf<String>()
var languages = listOf<String>()
private var currentQuery: String? = null
companion object {
@ -175,7 +176,7 @@ class PluginsViewModel : ViewModel() {
}
this.plugins = list
_filteredPlugins.postValue(false to list.filterTvTypes().sortByQuery(currentQuery))
_filteredPlugins.postValue(false to list.filterTvTypes().filterLang().sortByQuery(currentQuery))
}
// Perhaps can be optimized?
@ -187,6 +188,16 @@ class PluginsViewModel : ViewModel() {
}
}
private fun List<PluginViewData>.filterLang(): List<PluginViewData> {
if (languages.isEmpty()) return this
return this.filter {
if (it.plugin.second.language == null) {
return@filter languages.contains("none")
}
languages.contains(it.plugin.second.language)
}
}
private fun List<PluginViewData>.sortByQuery(query: String?): List<PluginViewData> {
return if (query == null) {
// Return list to base state if no query
@ -197,7 +208,7 @@ class PluginsViewModel : ViewModel() {
}
fun updateFilteredPlugins() {
_filteredPlugins.postValue(false to plugins.filterTvTypes().sortByQuery(currentQuery))
_filteredPlugins.postValue(false to plugins.filterTvTypes().filterLang().sortByQuery(currentQuery))
}
fun updatePluginList(repositoryUrl: String) = viewModelScope.launchSafe {
@ -223,6 +234,6 @@ class PluginsViewModel : ViewModel() {
}
plugins = downloadedPlugins
_filteredPlugins.postValue(false to downloadedPlugins.filterTvTypes().sortByQuery(currentQuery))
_filteredPlugins.postValue(false to downloadedPlugins.filterTvTypes().filterLang().sortByQuery(currentQuery))
}
}

View file

@ -288,7 +288,7 @@ object AppUtils {
return this.packageManager.hasSystemFeature("android.software.webview")
}
private fun openWebView(fragment: Fragment?, url: String) {
fun openWebView(fragment: Fragment?, url: String) {
if (fragment?.context?.hasWebView() == true)
normalSafeApiCall {
fragment

View file

@ -43,6 +43,13 @@ object Coroutines {
}
}
suspend fun <T, V> V.mainWork(work: suspend (CoroutineScope.(V) -> T)): T {
val value = this
return withContext(Dispatchers.Main) {
work(value)
}
}
fun runOnMainThread(work: (() -> Unit)) {
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post {

View file

@ -127,6 +127,7 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:clipToPadding="false"
android:id="@+id/plugin_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"

View file

@ -8,6 +8,11 @@
app:actionViewClass="android.widget.SearchView"
app:searchHintIcon="@drawable/search_icon"
app:showAsAction="ifRoom" />
<item
android:id="@+id/lang_filter"
android:icon="@drawable/ic_baseline_language_24"
android:title="@string/provider_lang_settings"
app:showAsAction="collapseActionView|ifRoom" />
<item
android:id="@+id/download_all"
android:icon="@drawable/netflix_download"

View file

@ -1,21 +1,8 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- KEYS DON'T TRANSLATE -->
<string name="bottom_title_key" translatable="false">bottom_title_key</string>
<string name="override_site_key" translatable="false">override_site_key</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Elenco: %s</string>
<string name="next_episode_format" formatted="true">O episódio %d vai ser lançado em</string>
@ -29,9 +16,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -54,7 +39,6 @@
<string name="no_data">Sem dados</string>
<string name="episode_more_options_des">Mais Opções</string>
<string name="next_episode">Próximo episódio</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Gêneros</string>
<string name="result_share">Compartilhar</string>
<string name="result_open_in_browser">Abrir no Navegador</string>
@ -86,7 +70,6 @@
<string name="download_failed">Download Falhado</string>
<string name="download_canceled">Download Cancelado</string>
<string name="download_done">Download Finalizado</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Transmitir</string>
<string name="error_loading_links_toast">Erro Carregando Links</string>
@ -236,7 +219,6 @@
<string name="delete_file">Deletar Arquivo</string>
<string name="delete">Deletar</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pausar</string>
<string name="resume">Retomar</string>
<string name="go_back_30">-30</string>
@ -303,10 +285,6 @@
<string name="show_dub">Etiqueta Dub</string>
<string name="show_sub">Etiqueta Sub</string>
<string name="show_title">Título</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">Alternar elementos da interface no pôster</string>
<string name="no_update_found">Nenhuma Atualização encontrada</string>
@ -350,7 +328,6 @@
<string name="resize_zoom">Zoom</string>
<string name="legal_notice">Aviso Legal</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.
@ -389,10 +366,6 @@
<!-- 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">senha123</string>
<string name="example_username">MeuNomeLegal</string>
<string name="example_email">oi@mundo.com</string>
@ -432,7 +405,6 @@
<string name="all">Tudo</string>
<string name="max">Max</string>
<string name="min">Min</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Contornado</string>
<string name="subtitles_depressed">Afundado</string>
<string name="subtitles_shadow">Sombreado</string>

View file

@ -3,17 +3,6 @@
<!-- KEYS DON'T TRANSLATE -->
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Hrají: %s</string>
@ -23,9 +12,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -48,7 +35,6 @@
<string name="no_data">Žádná data</string>
<string name="episode_more_options_des">Další možnosti</string>
<string name="next_episode">Další epizoda</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Žánry</string>
<string name="result_share">Sdílet</string>
<string name="result_open_in_browser">Otevřít v prohlížeči</string>
@ -80,7 +66,6 @@
<string name="download_failed">Stahování selhalo</string>
<string name="download_canceled">Stahování zrušeno</string>
<string name="download_done">Stahování dokončeno</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="error_loading_links_toast">Chyba při načítání odkazů</string>
<string name="download_storage_text">Interní úložiště</string>
@ -229,7 +214,6 @@
<string name="delete_file">Smazat soubor</string>
<string name="delete">Smazat</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pozastavit</string>
<string name="resume">Pokračovat</string>
<string name="go_back_30">-30</string>
@ -296,10 +280,6 @@
<string name="show_dub">Štítek dabingu</string>
<string name="show_sub">Štítek titulků</string>
<string name="show_title">Název</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">Přepnout prvky UI na plakátu</string>
<string name="no_update_found">Nenalezeny žádné aktualizace</string>
@ -339,7 +319,6 @@
<string name="resize_zoom">Přiblížit</string>
<string name="legal_notice">Odmítnutí odpovědnosti</string>
<string name="legal_notice_key" translatable="false">legal_notice_key</string>
<string name="legal_notice_text" translatable="false">Jakékoli právní otázky týkající se obsahu této aplikace
je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni.
@ -378,10 +357,6 @@
<!-- 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">heslo123</string>
<string name="example_username">MojeSuperJmeno</string>
<string name="example_email">ahoj@svete.cz</string>
@ -418,7 +393,6 @@
<string name="all">Vše</string>
<string name="max">Max</string>
<string name="min">Min</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Ohraničení</string>
<string name="subtitles_depressed">Potlačené</string>
<string name="subtitles_shadow">Stín</string>

View file

@ -1,17 +1,6 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Reparto: %s</string>
<string name="next_episode_format" formatted="true">El episodio %d se publicará en</string>
@ -26,9 +15,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -51,7 +38,6 @@
<string name="no_data">Sin datos</string>
<string name="episode_more_options_des">Más opciones</string>
<string name="next_episode">Siguiente episodio</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Géneros</string>
<string name="result_share">Compartir</string>
<string name="result_open_in_browser">Abrir en el navegador</string>
@ -83,7 +69,6 @@
<string name="download_failed">Descarga fallida</string>
<string name="download_canceled">Descarga cancelada</string>
<string name="download_done">Descarga realizada</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Transmitir</string>
<string name="error_loading_links_toast">Error al cargar enlaces</string>
@ -230,7 +215,6 @@
<string name="delete_file">Eliminar archivo</string>
<string name="delete">Eliminar</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pausar</string>
<string name="resume">Reanudar</string>
<string name="go_back_30">-30</string>
@ -297,10 +281,6 @@
<string name="show_dub">Etiqueta de doblaje</string>
<string name="show_sub">Etiqueta de subtitulado</string>
<string name="show_title">Título</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">Alternar elementos de la interfaz de usuario en el póster</string>
<string name="no_update_found">No se encontró ninguna actualización</string>
@ -344,7 +324,6 @@
<string name="resize_zoom">Zoom</string>
<string name="legal_notice">Descargo de responsabilidad</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.
@ -383,10 +362,6 @@
<!-- 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">contraseña123</string>
<string name="example_username">MiNombreDeUsuarioGenial</string>
<string name="example_email">hola@mundo.com</string>
@ -425,7 +400,6 @@
<string name="all">Todo</string>
<string name="max">Máximo</string>
<string name="min">Mínimo</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Contorno</string>
<string name="subtitles_depressed">Deprimido</string>
<string name="subtitles_shadow">Sombra</string>

View file

@ -293,5 +293,4 @@
<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="nginx_info_title">Qu\'est-ce qu\'Nginx ?</string>
</resources>

View file

@ -2,17 +2,6 @@
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Pemeran: %s</string>
@ -22,9 +11,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -47,7 +34,6 @@
<string name="no_data">Tidak Ada Data</string>
<string name="episode_more_options_des">Opsi Lanjutan</string>
<string name="next_episode">Episode selanjutnya</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Genre</string>
<string name="result_share">Bagikan</string>
<string name="result_open_in_browser">Buka Di Browser</string>
@ -79,7 +65,6 @@
<string name="download_failed">Download Gagal</string>
<string name="download_canceled">Download Dibatalkan</string>
<string name="download_done">Download Selesai</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="error_loading_links_toast">Error Memuat Tautan</string>
<string name="download_storage_text">Penyimpanan Internal</string>
@ -225,7 +210,6 @@
<string name="delete_file">Hapus File</string>
<string name="delete">Hapus</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Jeda</string>
<string name="resume">Lanjutkan</string>
<string name="go_back_30">-30</string>
@ -292,10 +276,6 @@
<string name="show_dub">Label dub</string>
<string name="show_sub">Label sub</string>
<string name="show_title">Judul</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">Aktifkan atau nonaktifkan elemen UI pada poster</string>
<string name="no_update_found">Tidak Ada Update Yang Ditemukan</string>
@ -335,7 +315,6 @@
<string name="resize_zoom">Zoom</string>
<string name="legal_notice">Peringatan</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.
@ -373,8 +352,6 @@
<!-- account stuff -->
<string name="anilist_key" translatable="false">anilist_key</string>
<string name="mal_key" translatable="false">mal_key</string>
<!--
<string name="mal_account_settings" translatable="false">MAL</string>
<string name="anilist_account_settings" translatable="false">AniList</string>
@ -405,7 +382,6 @@
<string name="all">Semua</string>
<string name="max">Maks</string>
<string name="min">Min</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Garis luar</string>
<string name="subtitles_depressed">Depresi</string>
<string name="subtitles_shadow">Bayangan</string>

View file

@ -1,16 +1,6 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Cast: %s</string>
<string name="next_episode_format" formatted="true">L\'episodio %d uscirà in</string>
@ -79,7 +69,6 @@
<string name="download_failed">Download fallito</string>
<string name="download_canceled">Download cancellato</string>
<string name="download_done">Download completato</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Stream</string>
<string name="error_loading_links_toast">Errore durante il caricamento dei link</string>
@ -227,7 +216,6 @@
<string name="delete_file">Elimina file</string>
<string name="delete">Elimina</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pausa</string>
<string name="resume">Riprendi</string>
<string name="go_back_30">-30</string>
@ -390,7 +378,6 @@
<string name="all">Tutto</string>
<string name="max">Max</string>
<string name="min">Min</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Contorno</string>
<string name="subtitles_depressed">Bassorilievo</string>
<string name="subtitles_shadow">Ombra</string>

View file

@ -1,17 +1,6 @@
<!--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 | %sMB</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %sGB</string>
<string name="download_size_format" formatted="true" translatable="false">%sMB / %sMB</string>
<string name="mb_format" formatted="true" translatable="false">%dMB</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 Ep %d</string>
<string name="cast_format" formatted="true">Cast: %s</string>
<string name="next_episode_format" formatted="true">Aflevering %d zal worden uitgebracht in</string>
@ -25,9 +14,7 @@
<string name="episode_poster_img_des">Aflevering Poster</string>
<string name="home_main_poster_img_des">Hoofdposter</string>
<string name="home_next_random_img_des">Volgende willekeurig</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Ga terug</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Wijzig provider</string>
<string name="preview_background_img_des">Voorbeeld Achtergrond</string>
@ -51,7 +38,6 @@
<string name="no_data">Geen gegevens</string>
<string name="episode_more_options_des">Meer Opties</string>
<string name="next_episode">Volgende aflevering</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Genres</string>
<string name="result_share">Deel</string>
<string name="result_open_in_browser">Openen in Browser</string>
@ -234,7 +220,6 @@
<string name="delete_file">Verwijder bestand</string>
<string name="delete">Verwijder</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pauze</string>
<string name="resume">Hervatten</string>
<string name="go_back_30">-30</string>
@ -302,10 +287,6 @@
<string name="show_dub">Dub label</string>
<string name="show_sub">Sub label</string>
<string name="show_title">Titel</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">Schakel UI-elementen op poster</string>
<string name="no_update_found">Geen update gevonden</string>
@ -371,10 +352,6 @@
<!-- 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">wachtwoord123</string>
<string name="example_username">MijnCoolGebruikersnaam</string>
<string name="example_email">hello@Wereld.com</string>
@ -414,7 +391,6 @@
<string name="all">Allemaal</string>
<string name="max">Max</string>
<string name="min">Min</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Overzicht</string>
<string name="subtitles_depressed">Depressed</string>
<string name="subtitles_shadow">Schaduw</string>

View file

@ -232,7 +232,6 @@
<string name="preferred_media_settings">Foretrukket Videoinnhold</string>
<string name="legal_notice" translatable="false">Disclaimer</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.

View file

@ -430,4 +430,10 @@
<string name="other_singular">Wideo</string>
<string name="view_public_repositories_button">Zobacz repozytoria społeczności</string>
<string name="view_public_repositories_button_short">Publiczna lista</string>
<string name="example_lang_name">Kod języka (en)</string>
<string name="subtitles_filter_lang">Filtrowanie wg preferowanego języka mediów</string>
<string name="uppercase_all_subtitles">Wszystkie napisy wielką literą</string>
<string name="download_all_plugins_from_repo">Pobrać wszystkie rozszerzenia z tego repozytorium?</string>
<string name="single_plugin_disabled">%s (Wyłączone)</string>
<string name="pref_filter_search_quality">Ukryj wybraną jakość wideo w wynikach wyszukiwania</string>
</resources>

View file

@ -1,17 +1,6 @@
<!--https://newbedev.com/concatenate-multiple-strings-in-xml-->
<resources>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string>
<string name="cast_format" formatted="true">Distribuție: %s</string>
<string name="next_episode_format" formatted="true">Episodul %d va fi lansat în</string>
@ -78,7 +67,6 @@
<string name="download_failed">Descărcare eșuată</string>
<string name="download_canceled">Descărcare anulată</string>
<string name="download_done">Descărcare finalizată</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Stream</string>
<string name="error_loading_links_toast">Eroare la încărcarea linkurilor</string>
@ -226,7 +214,6 @@
<string name="delete_file">Ștergeți fișierul</string>
<string name="delete">Ștergeți</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Pauză</string>
<string name="resume">Continuă</string>
<string name="go_back_30">-30</string>
@ -405,7 +392,6 @@
<string name="all">Tot</string>
<string name="max">Maxim</string>
<string name="min">Minim</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Contur</string>
<string name="subtitles_depressed">Deprimat</string>
<string name="subtitles_shadow">Umbră</string>

View file

@ -1,17 +1,6 @@
<!--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 | %sMB</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %sGB</string>
<string name="download_size_format" formatted="true" translatable="false">%sMB / %sMB</string>
<string name="mb_format" formatted="true" translatable="false">%dMB</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 Ep %d</string>
<string name="cast_format" formatted="true">Cast: %s</string>
<string name="next_episode_format" formatted="true">Bölüm %d şu tarihte yayınlanacak: </string>
@ -25,9 +14,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -51,7 +38,6 @@
<string name="no_data">Veri yok</string>
<string name="episode_more_options_des">Daha fazla seçenek</string>
<string name="next_episode">Sonraki bölüm</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Türler</string>
<string name="result_share">Paylaş</string>
<string name="result_open_in_browser">Tarayıcıda aç</string>
@ -84,7 +70,6 @@
<string name="download_failed">İndirme başarısız oldu</string>
<string name="download_canceled">İndirme iptal edildi</string>
<string name="download_done">İndirme bitti</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Yayınla</string>
<string name="error_loading_links_toast">Bağlantılar yüklenirken hata oluştu</string>
@ -232,7 +217,6 @@
<string name="delete_file">Dosyayı sil</string>
<string name="delete">Sil</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Durdur</string>
<string name="resume">Sürdür</string>
<string name="go_back_30">-30</string>
@ -300,10 +284,6 @@
<string name="show_dub">Dublaj etiketi</string>
<string name="show_sub">Alt yazı etiketi</string>
<string name="show_title">Başlık</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">Poster üzerindeki öğeler</string>
<string name="no_update_found">Güncelleme bulunamadı</string>
@ -347,7 +327,6 @@
<string name="resize_zoom">Yakınlaştır</string>
<string name="legal_notice">Disclaimer</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.
@ -386,10 +365,6 @@
<!-- 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">şifre123</string>
<string name="example_username">HavalıKullanıcıAdı</string>
<string name="example_email">hello@world.com</string>
@ -429,7 +404,6 @@
<string name="all">Hepsi</string>
<string name="max">Maksimum</string>
<string name="min">Minimum</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Dış hat</string>
<string name="subtitles_depressed">Çökmüş</string>
<string name="subtitles_shadow">Gölge</string>

View file

@ -3,17 +3,6 @@
<!-- KEYS DON'T TRANSLATE -->
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" translatable="false" formatted="true">%d %s | %sMB</string>
<string name="storage_size_format" translatable="false" formatted="true">%s • %sGB</string>
<string name="download_size_format" translatable="false" formatted="true">%sMB / %sMB</string>
<string name="mb_format" translatable="false" formatted="true">%dMB</string>
<string name="episode_name_format" translatable="false" formatted="true">%s %s</string>
<string name="ffw_text_format" translatable="false" formatted="true">+%d</string>
<string name="rew_text_format" translatable="false" formatted="true">-%d</string>
<string name="ffw_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rew_text_regular_format" translatable="false" formatted="true">%d</string>
<string name="rating_format" translatable="false" formatted="true">%.1f/10.0</string>
<string name="year_format" translatable="false" formatted="true">%d</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%s Tập %d</string>
<string name="cast_format" formatted="true">Diễn viên: %s</string>
<string name="next_episode_format" formatted="true">Tập %d sẽ ra mắt sau</string>
@ -27,9 +16,7 @@
<string name="episode_poster_img_des">Episode Poster</string>
<string name="home_main_poster_img_des">Main Poster</string>
<string name="home_next_random_img_des">Next Random</string>
<string name="episode_play_img_des" translatable="false">@string/play_episode</string>
<string name="go_back_img_des">Go back</string>
<string name="change_providers_img_des" translatable="false">@string/home_change_provider_img_des</string>
<string name="home_change_provider_img_des">Change Provider</string>
<string name="preview_background_img_des">Preview Background</string>
@ -52,7 +39,6 @@
<string name="no_data">Không có dữ liệu</string>
<string name="episode_more_options_des">Thêm tuỳ chọn</string>
<string name="next_episode">Tập tiếp theo</string>
<string name="result_plot" translatable="false">@string/synopsis</string>
<string name="result_tags">Thể loại</string>
<string name="result_share">Chia sẻ</string>
<string name="result_open_in_browser">Mở bằng trình duyệt</string>
@ -84,7 +70,6 @@
<string name="download_failed">Tải lỗi</string>
<string name="download_canceled">Đã hủy</string>
<string name="download_done">Tải thành công</string>
<string name="download_format" translatable="false">%s - %s</string>
<string name="stream">Stream</string>
<string name="error_loading_links_toast">Đã có lỗi xảy ra</string>
@ -232,7 +217,6 @@
<string name="delete_file">Xóa Tệp</string>
<string name="delete">Xóa</string>
<string name="cancel" translatable="false">@string/sort_cancel</string>
<string name="pause">Tạm Dừng</string>
<string name="resume">Tiếp Tục</string>
<string name="go_back_30">-30</string>
@ -298,10 +282,6 @@
<string name="show_dub">Nhãn lồng tiếng</string>
<string name="show_sub">Nhãn phụ đề</string>
<string name="show_title">Tiêu đề</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">Thay đổi giao diện trên poster</string>
<string name="no_update_found">Bạn đang dùng phiên bản mới nhất</string>
@ -345,7 +325,6 @@
<string name="resize_zoom">Phóng to</string>
<string name="legal_notice">Disclaimer</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.
@ -383,10 +362,6 @@
<string name="bottom_title_settings_des">Đặt tiêu đề dưới poster</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">Mật khẩu</string>
<string name="example_username">Tài khoản</string>
<string name="example_email">Email</string>
@ -426,7 +401,6 @@
<string name="all">Tất cả</string>
<string name="max">Tối đa</string>
<string name="min">Tối thiểu</string>
<string name="subtitles_none" translatable="false">@string/none</string>
<string name="subtitles_outline">Viền</string>
<string name="subtitles_depressed">Chìm</string>
<string name="subtitles_shadow">Đổ bóng</string>

View file

@ -10,9 +10,7 @@
<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>
@ -33,7 +31,6 @@
<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>
@ -65,7 +62,6 @@
<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="error_loading_links_toast">加载链接时出错</string>
<string name="download_storage_text">内部存储</string>
@ -196,7 +192,6 @@
<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>
@ -287,7 +282,6 @@
<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.
@ -318,8 +312,6 @@
<string name="app_theme_settings">应用主题</string>
<!-- account stuff -->
<string name="anilist_key" translatable="false">anilist_key</string>
<string name="mal_key" translatable="false">mal_key</string>
<!--
<string name="mal_account_settings" translatable="false">MAL</string>
<string name="anilist_account_settings" translatable="false">AniList</string>
@ -344,7 +336,6 @@
<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>

View file

@ -56,10 +56,9 @@
<string name="pref_filter_search_quality_key" translatable="false">pref_filter_search_quality_key</string>
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %sMB</string>
<string name="storage_size_format" formatted="true" translatable="false">%s • %sGB</string>
<string name="download_size_format" formatted="true" translatable="false">%sMB / %sMB</string>
<string name="mb_format" formatted="true" translatable="false">%dMB</string>
<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>