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>
@ -50,9 +37,8 @@
<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="next_episode">Siguiente episodio</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>
<string name="skip_loading">Omitir carga</string>
@ -82,8 +68,7 @@
<string name="download_started">Descarga iniciada</string>
<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="download_done">Descarga realizada</string>
<string name="stream">Transmitir</string>
<string name="error_loading_links_toast">Error al cargar enlaces</string>
@ -229,9 +214,8 @@
<string name="no_episodes_found">No se encontraron episodios</string>
<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="delete">Eliminar</string>
<string name="pause">Pausar</string>
<string name="resume">Reanudar</string>
<string name="go_back_30">-30</string>
<string name="go_forward_30">+30</string>
@ -296,12 +280,8 @@
<string name="show_hd">Etiqueta de calidad</string>
<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="show_title">Título</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>
<string name="check_for_update">Buscar actualizaciones</string>
@ -343,9 +323,8 @@
<string name="resize_fill">Estirar</string>
<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
<string name="legal_notice">Descargo de responsabilidad</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.
@ -382,11 +361,7 @@
<string name="bottom_title_settings_des">Coloca el título debajo del póster</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>
<!-- account stuff -->
<string name="example_password">contraseña123</string>
<string name="example_username">MiNombreDeUsuarioGenial</string>
<string name="example_email">hola@mundo.com</string>
@ -424,9 +399,8 @@
<string name="normal">Normal</string>
<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="min">Mínimo</string>
<string name="subtitles_outline">Contorno</string>
<string name="subtitles_depressed">Deprimido</string>
<string name="subtitles_shadow">Sombra</string>
<string name="subtitles_raised">Elevado</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>