mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
3d398f0b92
72 changed files with 2884 additions and 1790 deletions
6
app/CMakeLists.txt
Normal file
6
app/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Set this to the minimum version your project supports.
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(CrashHandler)
|
||||
find_library(log-lib log)
|
||||
add_library(native-lib SHARED src/main/cpp/native-lib.cpp)
|
||||
target_link_libraries(native-lib ${log-lib})
|
|
@ -32,6 +32,13 @@ android {
|
|||
enable = true
|
||||
}
|
||||
|
||||
// disable this for now
|
||||
//externalNativeBuild {
|
||||
// cmake {
|
||||
// path("CMakeLists.txt")
|
||||
// }
|
||||
//}
|
||||
|
||||
signingConfigs {
|
||||
create("prerelease") {
|
||||
if (prereleaseStoreFile != null) {
|
||||
|
@ -49,10 +56,10 @@ android {
|
|||
defaultConfig {
|
||||
applicationId = "com.lagradost.cloudstream3"
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
targetSdk = 29
|
||||
|
||||
versionCode = 59
|
||||
versionName = "4.1.3"
|
||||
versionName = "4.1.8"
|
||||
|
||||
resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}")
|
||||
resValue("string", "commit_hash", "git rev-parse --short HEAD".execute() ?: "")
|
||||
|
@ -165,7 +172,7 @@ dependencies {
|
|||
androidTestImplementation("androidx.test:core")
|
||||
|
||||
//implementation("io.karn:khttp-android:0.1.2") //okhttp instead
|
||||
// implementation("org.jsoup:jsoup:1.13.1")
|
||||
// implementation("org.jsoup:jsoup:1.13.1")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.1")
|
||||
|
||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
||||
|
@ -181,22 +188,22 @@ dependencies {
|
|||
// implementation("androidx.leanback:leanback-paging:1.1.0-alpha09")
|
||||
|
||||
// Media 3
|
||||
implementation("androidx.media3:media3-common:1.1.0")
|
||||
implementation("androidx.media3:media3-exoplayer:1.1.0")
|
||||
implementation("androidx.media3:media3-datasource-okhttp:1.1.0")
|
||||
implementation("androidx.media3:media3-ui:1.1.0")
|
||||
implementation("androidx.media3:media3-session:1.1.0")
|
||||
implementation("androidx.media3:media3-cast:1.1.0")
|
||||
implementation("androidx.media3:media3-exoplayer-hls:1.1.0")
|
||||
implementation("androidx.media3:media3-exoplayer-dash:1.1.0")
|
||||
implementation("androidx.media3:media3-common:1.1.1")
|
||||
implementation("androidx.media3:media3-exoplayer:1.1.1")
|
||||
implementation("androidx.media3:media3-datasource-okhttp:1.1.1")
|
||||
implementation("androidx.media3:media3-ui:1.1.1")
|
||||
implementation("androidx.media3:media3-session:1.1.1")
|
||||
implementation("androidx.media3:media3-cast:1.1.1")
|
||||
implementation("androidx.media3:media3-exoplayer-hls:1.1.1")
|
||||
implementation("androidx.media3:media3-exoplayer-dash:1.1.1")
|
||||
// Custom ffmpeg extension for audio codecs
|
||||
implementation("com.github.recloudstream:media-ffmpeg:1.1.0")
|
||||
|
||||
//implementation("com.google.android.exoplayer:extension-leanback:2.14.0")
|
||||
|
||||
// Bug reports
|
||||
implementation("ch.acra:acra-core:5.8.4")
|
||||
implementation("ch.acra:acra-toast:5.8.4")
|
||||
implementation("ch.acra:acra-core:5.11.0")
|
||||
implementation("ch.acra:acra-toast:5.11.0")
|
||||
|
||||
compileOnly("com.google.auto.service:auto-service-annotations:1.0")
|
||||
//either for java sources:
|
||||
|
@ -220,18 +227,18 @@ dependencies {
|
|||
implementation("androidx.work:work-runtime-ktx:2.8.1")
|
||||
|
||||
// Networking
|
||||
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
||||
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
|
||||
// implementation("com.squareup.okhttp3:okhttp:4.9.2")
|
||||
// implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1")
|
||||
implementation("com.github.Blatzar:NiceHttp:0.4.3")
|
||||
// To fix SSL fuckery on android 9
|
||||
implementation("org.conscrypt:conscrypt-android:2.2.1")
|
||||
// Util to skip the URI file fuckery 🙏
|
||||
implementation("com.github.tachiyomiorg:unifile:17bec43")
|
||||
implementation("com.github.LagradOst:SafeFile:0.0.2")
|
||||
|
||||
// API because cba maintaining it myself
|
||||
implementation("com.uwetrottmann.tmdb2:tmdb-java:2.6.0")
|
||||
|
||||
implementation("com.github.discord:OverlappingPanels:0.1.3")
|
||||
implementation("com.github.discord:OverlappingPanels:0.1.5")
|
||||
// debugImplementation because LeakCanary should only run in debug builds.
|
||||
//debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
|
||||
|
||||
|
@ -243,11 +250,9 @@ dependencies {
|
|||
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
|
||||
implementation("com.github.albfernandez:juniversalchardet:2.4.0")
|
||||
|
||||
// slow af yt
|
||||
//implementation("com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT")
|
||||
|
||||
// newpipe yt taken from https://github.com/TeamNewPipe/NewPipe/blob/dev/app/build.gradle#L204
|
||||
implementation("com.github.TeamNewPipe:NewPipeExtractor:8495ad619e")
|
||||
// this should be updated frequently to avoid trailer fu*kery
|
||||
implementation("com.github.TeamNewPipe:NewPipeExtractor:1f08d28")
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
|
||||
|
||||
// Library/extensions searching with Levenshtein distance
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" /> <!-- Used for updates without prompt -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Used for update service -->
|
||||
|
||||
<!-- <permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!– Used for getting if vlc is installed –> -->
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Required for getting arbitrary Aniyomi packages -->
|
||||
<!-- Fixes android tv fuckery -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.touchscreen"
|
||||
|
|
28
app/src/main/cpp/native-lib.cpp
Normal file
28
app/src/main/cpp/native-lib.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include <jni.h>
|
||||
#include <csignal>
|
||||
#include <android/log.h>
|
||||
|
||||
#define TAG "CloudStream Crash Handler"
|
||||
volatile sig_atomic_t gSignalStatus = 0;
|
||||
void handleNativeCrash(int signal) {
|
||||
gSignalStatus = signal;
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_lagradost_cloudstream3_NativeCrashHandler_initNativeCrashHandler(JNIEnv *env, jobject) {
|
||||
#define REGISTER_SIGNAL(X) signal(X, handleNativeCrash);
|
||||
REGISTER_SIGNAL(SIGSEGV)
|
||||
#undef REGISTER_SIGNAL
|
||||
}
|
||||
|
||||
//extern "C" JNIEXPORT void JNICALL
|
||||
//Java_com_lagradost_cloudstream3_NativeCrashHandler_triggerNativeCrash(JNIEnv *env, jobject thiz) {
|
||||
// int *p = nullptr;
|
||||
// *p = 0;
|
||||
//}
|
||||
|
||||
extern "C" JNIEXPORT int JNICALL
|
||||
Java_com_lagradost_cloudstream3_NativeCrashHandler_getSignalStatus(JNIEnv *env, jobject) {
|
||||
//__android_log_print(ANDROID_LOG_INFO, TAG, "Got signal status %d", gSignalStatus);
|
||||
return gSignalStatus;
|
||||
}
|
|
@ -43,9 +43,9 @@ class CustomReportSender : ReportSender {
|
|||
override fun send(context: Context, errorContent: CrashReportData) {
|
||||
println("Sending report")
|
||||
val url =
|
||||
"https://docs.google.com/forms/d/e/1FAIpQLSdOlbgCx7NeaxjvEGyEQlqdh2nCvwjm2vwpP1VwW7REj9Ri3Q/formResponse"
|
||||
"https://docs.google.com/forms/d/e/1FAIpQLSfO4r353BJ79TTY_-t5KWSIJT2xfqcQWY81xjAA1-1N0U2eSg/formResponse"
|
||||
val data = mapOf(
|
||||
"entry.753293084" to errorContent.toJSON()
|
||||
"entry.1993829403" to errorContent.toJSON()
|
||||
)
|
||||
|
||||
thread { // to not run it on main thread
|
||||
|
@ -104,12 +104,17 @@ class ExceptionHandler(val errorFile: File, val onError: (() -> Unit)) :
|
|||
}
|
||||
|
||||
class AcraApplication : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Thread.setDefaultUncaughtExceptionHandler(ExceptionHandler(filesDir.resolve("last_error")) {
|
||||
//NativeCrashHandler.initCrashHandler()
|
||||
ExceptionHandler(filesDir.resolve("last_error")) {
|
||||
val intent = context!!.packageManager.getLaunchIntentForPackage(context!!.packageName)
|
||||
startActivity(Intent.makeRestartActivityTask(intent!!.component))
|
||||
})
|
||||
}.also {
|
||||
exceptionHandler = it
|
||||
Thread.setDefaultUncaughtExceptionHandler(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
|
@ -121,10 +126,10 @@ class AcraApplication : Application() {
|
|||
buildConfigClass = BuildConfig::class.java
|
||||
reportFormat = StringFormat.JSON
|
||||
|
||||
reportContent = arrayOf(
|
||||
reportContent = listOf(
|
||||
ReportField.BUILD_CONFIG, ReportField.USER_CRASH_DATE,
|
||||
ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL,
|
||||
ReportField.STACK_TRACE
|
||||
ReportField.STACK_TRACE,
|
||||
)
|
||||
|
||||
// removed this due to bug when starting the app, moved it to when it actually crashes
|
||||
|
@ -137,6 +142,8 @@ class AcraApplication : Application() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
var exceptionHandler: ExceptionHandler? = null
|
||||
|
||||
/** Use to get activity from Context */
|
||||
tailrec fun Context.getActivity(): Activity? = this as? Activity
|
||||
?: (this as? ContextWrapper)?.baseContext?.getActivity()
|
||||
|
@ -211,6 +218,5 @@ class AcraApplication : Application() {
|
|||
activity?.supportFragmentManager?.fragments?.lastOrNull()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -50,7 +50,7 @@ class DownloaderTestImpl private constructor(builder: OkHttpClient.Builder) : Do
|
|||
|
||||
companion object {
|
||||
private const val USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
|
||||
private var instance: DownloaderTestImpl? = null
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,7 +29,7 @@ import java.util.*
|
|||
import kotlin.math.absoluteValue
|
||||
|
||||
const val USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
|
||||
|
||||
//val baseHeader = mapOf("User-Agent" to USER_AGENT)
|
||||
val mapper = JsonMapper.builder().addModule(KotlinModule())
|
||||
|
|
|
@ -144,6 +144,7 @@ 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 com.lagradost.safefile.SafeFile
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.io.File
|
||||
|
@ -279,6 +280,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
companion object {
|
||||
const val TAG = "MAINACT"
|
||||
var lastError: String? = null
|
||||
|
||||
/**
|
||||
* Setting this will automatically enter the query in the search
|
||||
* next time the search fragment is opened.
|
||||
|
@ -286,7 +288,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
*
|
||||
* This is a very bad solution but I was unable to find a better one.
|
||||
**/
|
||||
private var nextSearchQuery: String? = null
|
||||
var nextSearchQuery: String? = null
|
||||
|
||||
/**
|
||||
* Fires every time a new batch of plugins have been loaded, no guarantee about how often this is run and on which thread
|
||||
|
@ -362,9 +364,14 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
loadRepository(url)
|
||||
return true
|
||||
} else if (safeURI(str)?.scheme == appStringSearch) {
|
||||
val query = str.substringAfter("$appStringSearch://")
|
||||
nextSearchQuery =
|
||||
URLDecoder.decode(str.substringAfter("$appStringSearch://"), "UTF-8")
|
||||
|
||||
try {
|
||||
URLDecoder.decode(query, "UTF-8")
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
query
|
||||
}
|
||||
// Use both navigation views to support both layouts.
|
||||
// It might be better to use the QuickSearch.
|
||||
activity?.findViewById<BottomNavigationView>(R.id.nav_view)?.selectedItemId =
|
||||
|
@ -854,7 +861,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
RecyclerView::class.java.declaredMethods.firstOrNull {
|
||||
it.name == "scrollStep"
|
||||
}?.also { it.isAccessible = true }
|
||||
} catch (t : Throwable) {
|
||||
} catch (t: Throwable) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
@ -901,11 +908,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
if (dx > 0) dx else 0
|
||||
}
|
||||
|
||||
if(!NO_MOVE_LIST) {
|
||||
if (!NO_MOVE_LIST) {
|
||||
parent.smoothScrollBy(rdx, 0)
|
||||
}else {
|
||||
} else {
|
||||
val smoothScroll = reflectedScroll
|
||||
if(smoothScroll == null) {
|
||||
if (smoothScroll == null) {
|
||||
parent.smoothScrollBy(rdx, 0)
|
||||
} else {
|
||||
try {
|
||||
|
@ -915,12 +922,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
val out = IntArray(2)
|
||||
smoothScroll.invoke(parent, rdx, 0, out)
|
||||
val scrolledX = out[0]
|
||||
if(abs(scrolledX) <= 0) { // newFocus.measuredWidth*2
|
||||
if (abs(scrolledX) <= 0) { // newFocus.measuredWidth*2
|
||||
smoothScroll.invoke(parent, -rdx, 0, out)
|
||||
parent.smoothScrollBy(scrolledX, 0)
|
||||
if (NO_MOVE_LIST) targetDx = scrolledX
|
||||
}
|
||||
} catch (t : Throwable) {
|
||||
} catch (t: Throwable) {
|
||||
parent.smoothScrollBy(rdx, 0)
|
||||
}
|
||||
}
|
||||
|
@ -1126,10 +1133,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
snackbar.show()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ioSafe { SafeFile.check(this@MainActivity) }
|
||||
|
||||
if (PluginManager.checkSafeModeFile()) {
|
||||
normalSafeApiCall {
|
||||
|
@ -1315,7 +1322,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
if (navDestination.matchDestination(R.id.navigation_search) && !nextSearchQuery.isNullOrBlank()) {
|
||||
bundle?.apply {
|
||||
this.putString(SearchFragment.SEARCH_QUERY, nextSearchQuery)
|
||||
nextSearchQuery = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package com.lagradost.cloudstream3
|
||||
|
||||
import com.lagradost.cloudstream3.MainActivity.Companion.lastError
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.plugins.PluginManager.checkSafeModeFile
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object NativeCrashHandler {
|
||||
// external fun triggerNativeCrash()
|
||||
/*private external fun initNativeCrashHandler()
|
||||
private external fun getSignalStatus(): Int
|
||||
|
||||
private fun initSignalPolling() = CoroutineScope(Dispatchers.IO).launch {
|
||||
|
||||
//launch {
|
||||
// delay(10000)
|
||||
// triggerNativeCrash()
|
||||
//}
|
||||
|
||||
while (true) {
|
||||
delay(10_000)
|
||||
val signal = getSignalStatus()
|
||||
// Signal is initialized to zero
|
||||
if (signal == 0) continue
|
||||
|
||||
// Do not crash in safe mode!
|
||||
if (lastError != null) continue
|
||||
if (checkSafeModeFile()) continue
|
||||
|
||||
AcraApplication.exceptionHandler?.uncaughtException(
|
||||
Thread.currentThread(),
|
||||
RuntimeException("Native crash with code: $signal. Try uninstalling extensions.\n")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun initCrashHandler() {
|
||||
try {
|
||||
System.loadLibrary("native-lib")
|
||||
initNativeCrashHandler()
|
||||
} catch (t: Throwable) {
|
||||
// Make debug crash.
|
||||
if (BuildConfig.DEBUG) throw t
|
||||
logError(t)
|
||||
return
|
||||
}
|
||||
|
||||
initSignalPolling()
|
||||
}*/
|
||||
}
|
|
@ -1,13 +1,11 @@
|
|||
package com.lagradost.cloudstream3.extractors
|
||||
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.USER_AGENT
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorApi
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson
|
||||
import android.util.Log
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import java.net.URLDecoder
|
||||
|
||||
open class Cda: ExtractorApi() {
|
||||
|
|
|
@ -10,24 +10,39 @@ open class Mp4Upload : ExtractorApi() {
|
|||
override var name = "Mp4Upload"
|
||||
override var mainUrl = "https://www.mp4upload.com"
|
||||
private val srcRegex = Regex("""player\.src\("(.*?)"""")
|
||||
override val requiresReferer = true
|
||||
private val srcRegex2 = Regex("""player\.src\([\w\W]*src: "(.*?)"""")
|
||||
|
||||
override val requiresReferer = true
|
||||
private val idMatch = Regex("""mp4upload\.com/(embed-|)([A-Za-z0-9]*)""")
|
||||
override suspend fun getUrl(url: String, referer: String?): List<ExtractorLink>? {
|
||||
with(app.get(url)) {
|
||||
getAndUnpack(this.text).let { unpackedText ->
|
||||
val quality = unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull()
|
||||
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
|
||||
return listOf(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
link,
|
||||
url,
|
||||
quality ?: Qualities.Unknown.value,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
val realUrl = idMatch.find(url)?.groupValues?.get(2)?.let { id ->
|
||||
"$mainUrl/embed-$id.html"
|
||||
} ?: url
|
||||
val response = app.get(realUrl)
|
||||
val unpackedText = getAndUnpack(response.text)
|
||||
val quality =
|
||||
unpackedText.lowercase().substringAfter(" height=").substringBefore(" ").toIntOrNull()
|
||||
srcRegex.find(unpackedText)?.groupValues?.get(1)?.let { link ->
|
||||
return listOf(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
link,
|
||||
url,
|
||||
quality ?: Qualities.Unknown.value,
|
||||
)
|
||||
)
|
||||
}
|
||||
srcRegex2.find(unpackedText)?.groupValues?.get(1)?.let { link ->
|
||||
return listOf(
|
||||
ExtractorLink(
|
||||
name,
|
||||
name,
|
||||
link,
|
||||
url,
|
||||
quality ?: Qualities.Unknown.value,
|
||||
)
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ open class Rabbitstream : ExtractorApi() {
|
|||
override val requiresReferer = false
|
||||
open val embed = "ajax/embed-4"
|
||||
open val key = "https://raw.githubusercontent.com/enimax-anime/key/e4/key.txt"
|
||||
private var rawKey: String? = null
|
||||
|
||||
override suspend fun getUrl(
|
||||
url: String,
|
||||
|
@ -82,9 +81,10 @@ open class Rabbitstream : ExtractorApi() {
|
|||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private suspend fun getRawKey(): String = rawKey ?: app.get(key).text.also { rawKey = it }
|
||||
private suspend fun getRawKey(): String = app.get(key).text
|
||||
|
||||
private fun extractRealKey(originalString: String?, stops: String): Pair<String, String> {
|
||||
val table = parseJson<List<List<Int>>>(stops)
|
||||
|
|
|
@ -23,7 +23,7 @@ data class VisualDownloadChildCached(
|
|||
val data: VideoDownloadHelper.DownloadEpisodeCached,
|
||||
)
|
||||
|
||||
data class DownloadClickEvent(val action: Int, val data: EasyDownloadButton.IMinimumData)
|
||||
data class DownloadClickEvent(val action: Int, val data: VideoDownloadHelper.DownloadEpisodeCached)
|
||||
|
||||
class DownloadChildAdapter(
|
||||
var cardList: List<VisualDownloadChildCached>,
|
||||
|
|
|
@ -1,264 +0,0 @@
|
|||
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
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.ContentLoadingProgressBar
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.utils.Coroutines
|
||||
import com.lagradost.cloudstream3.utils.IDisposable
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||
|
||||
class EasyDownloadButton : IDisposable {
|
||||
interface IMinimumData {
|
||||
val id: Int
|
||||
}
|
||||
|
||||
private var _clickCallback: ((DownloadClickEvent) -> Unit)? = null
|
||||
private var _imageChangeCallback: ((Pair<Int, String>) -> Unit)? = null
|
||||
|
||||
override fun dispose() {
|
||||
try {
|
||||
_clickCallback = null
|
||||
_imageChangeCallback = null
|
||||
downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent -= it }
|
||||
downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent -= it }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private var downloadProgressEventListener: ((Triple<Int, Long, Long>) -> Unit)? = null
|
||||
private var downloadStatusEventListener: ((Pair<Int, VideoDownloadManager.DownloadType>) -> Unit)? =
|
||||
null
|
||||
|
||||
fun setUpMaterialButton(
|
||||
setupCurrentBytes: Long?,
|
||||
setupTotalBytes: Long?,
|
||||
progressBar: ContentLoadingProgressBar,
|
||||
downloadButton: MaterialButton,
|
||||
textView: TextView?,
|
||||
data: IMinimumData,
|
||||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
) {
|
||||
setUpDownloadButton(
|
||||
setupCurrentBytes,
|
||||
setupTotalBytes,
|
||||
progressBar,
|
||||
textView,
|
||||
data,
|
||||
downloadButton,
|
||||
{
|
||||
downloadButton.setIconResource(it.first)
|
||||
downloadButton.text = it.second
|
||||
},
|
||||
clickCallback
|
||||
)
|
||||
}
|
||||
|
||||
fun setUpMoreButton(
|
||||
setupCurrentBytes: Long?,
|
||||
setupTotalBytes: Long?,
|
||||
progressBar: ContentLoadingProgressBar,
|
||||
downloadImage: ImageView,
|
||||
textView: TextView?,
|
||||
textViewProgress: TextView?,
|
||||
clickableView: View,
|
||||
isTextPercentage: Boolean,
|
||||
data: IMinimumData,
|
||||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
) {
|
||||
setUpDownloadButton(
|
||||
setupCurrentBytes,
|
||||
setupTotalBytes,
|
||||
progressBar,
|
||||
textViewProgress,
|
||||
data,
|
||||
clickableView,
|
||||
{ (image, text) ->
|
||||
downloadImage.isVisible = textViewProgress?.isGone ?: true
|
||||
downloadImage.setImageResource(image)
|
||||
textView?.text = text
|
||||
},
|
||||
clickCallback, isTextPercentage
|
||||
)
|
||||
}
|
||||
|
||||
fun setUpButton(
|
||||
setupCurrentBytes: Long?,
|
||||
setupTotalBytes: Long?,
|
||||
progressBar: ContentLoadingProgressBar,
|
||||
downloadImage: ImageView,
|
||||
textView: TextView?,
|
||||
data: IMinimumData,
|
||||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
) {
|
||||
setUpDownloadButton(
|
||||
setupCurrentBytes,
|
||||
setupTotalBytes,
|
||||
progressBar,
|
||||
textView,
|
||||
data,
|
||||
downloadImage,
|
||||
{
|
||||
downloadImage.setImageResource(it.first)
|
||||
},
|
||||
clickCallback
|
||||
)
|
||||
}
|
||||
|
||||
private fun setUpDownloadButton(
|
||||
setupCurrentBytes: Long?,
|
||||
setupTotalBytes: Long?,
|
||||
progressBar: ContentLoadingProgressBar,
|
||||
textView: TextView?,
|
||||
data: IMinimumData,
|
||||
downloadView: View,
|
||||
downloadImageChangeCallback: (Pair<Int, String>) -> Unit,
|
||||
clickCallback: (DownloadClickEvent) -> Unit,
|
||||
isTextPercentage: Boolean = false
|
||||
) {
|
||||
_clickCallback = clickCallback
|
||||
_imageChangeCallback = downloadImageChangeCallback
|
||||
var lastState: VideoDownloadManager.DownloadType? = null
|
||||
var currentBytes = setupCurrentBytes ?: 0
|
||||
var totalBytes = setupTotalBytes ?: 0
|
||||
var needImageUpdate = true
|
||||
|
||||
fun changeDownloadImage(state: VideoDownloadManager.DownloadType) {
|
||||
lastState = state
|
||||
if (currentBytes <= 0) needImageUpdate = true
|
||||
val img = if (currentBytes > 0) {
|
||||
when (state) {
|
||||
VideoDownloadManager.DownloadType.IsPaused -> Pair(
|
||||
R.drawable.ic_baseline_play_arrow_24,
|
||||
R.string.download_paused
|
||||
)
|
||||
VideoDownloadManager.DownloadType.IsDownloading -> Pair(
|
||||
R.drawable.netflix_pause,
|
||||
R.string.downloading
|
||||
)
|
||||
else -> Pair(R.drawable.ic_baseline_delete_outline_24, R.string.downloaded)
|
||||
}
|
||||
} else {
|
||||
Pair(R.drawable.netflix_download, R.string.download)
|
||||
}
|
||||
_imageChangeCallback?.invoke(
|
||||
Pair(
|
||||
img.first,
|
||||
downloadView.context.getString(img.second)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun fixDownloadedBytes(setCurrentBytes: Long, setTotalBytes: Long, animate: Boolean) {
|
||||
currentBytes = setCurrentBytes
|
||||
totalBytes = setTotalBytes
|
||||
|
||||
if (currentBytes == 0L) {
|
||||
changeDownloadImage(VideoDownloadManager.DownloadType.IsStopped)
|
||||
textView?.visibility = View.GONE
|
||||
progressBar.visibility = View.GONE
|
||||
} else {
|
||||
if (lastState == VideoDownloadManager.DownloadType.IsStopped) {
|
||||
changeDownloadImage(VideoDownloadManager.getDownloadState(data.id))
|
||||
}
|
||||
textView?.visibility = View.VISIBLE
|
||||
progressBar.visibility = View.VISIBLE
|
||||
val currentMbString = formatShortFileSize(textView?.context, setCurrentBytes)
|
||||
val totalMbString = formatShortFileSize(textView?.context, setTotalBytes)
|
||||
|
||||
textView?.text =
|
||||
if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else
|
||||
textView?.context?.getString(R.string.download_size_format)
|
||||
?.format(currentMbString, totalMbString)
|
||||
|
||||
progressBar.let { bar ->
|
||||
bar.max = (setTotalBytes / 1000).toInt()
|
||||
|
||||
if (animate) {
|
||||
val animation: ObjectAnimator = ObjectAnimator.ofInt(
|
||||
bar,
|
||||
"progress",
|
||||
bar.progress,
|
||||
(setCurrentBytes / 1000).toInt()
|
||||
)
|
||||
animation.duration = 500
|
||||
animation.setAutoCancel(true)
|
||||
animation.interpolator = DecelerateInterpolator()
|
||||
animation.start()
|
||||
} else {
|
||||
bar.progress = (setCurrentBytes / 1000).toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixDownloadedBytes(currentBytes, totalBytes, false)
|
||||
changeDownloadImage(VideoDownloadManager.getDownloadState(data.id))
|
||||
|
||||
downloadProgressEventListener = { downloadData: Triple<Int, Long, Long> ->
|
||||
if (data.id == downloadData.first) {
|
||||
if (downloadData.second != currentBytes || downloadData.third != totalBytes) { // TO PREVENT WASTING UI TIME
|
||||
Coroutines.runOnMainThread {
|
||||
fixDownloadedBytes(downloadData.second, downloadData.third, true)
|
||||
changeDownloadImage(VideoDownloadManager.getDownloadState(data.id))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadStatusEventListener =
|
||||
{ downloadData: Pair<Int, VideoDownloadManager.DownloadType> ->
|
||||
if (data.id == downloadData.first) {
|
||||
if (lastState != downloadData.second || needImageUpdate) { // TO PREVENT WASTING UI TIME
|
||||
Coroutines.runOnMainThread {
|
||||
changeDownloadImage(downloadData.second)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadProgressEventListener?.let { VideoDownloadManager.downloadProgressEvent += it }
|
||||
downloadStatusEventListener?.let { VideoDownloadManager.downloadStatusEvent += it }
|
||||
|
||||
downloadView.setOnClickListener {
|
||||
if (currentBytes <= 0 || totalBytes <= 0) {
|
||||
_clickCallback?.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data))
|
||||
} else {
|
||||
val list = arrayListOf(
|
||||
Pair(DOWNLOAD_ACTION_PLAY_FILE, R.string.popup_play_file),
|
||||
Pair(DOWNLOAD_ACTION_DELETE_FILE, R.string.popup_delete_file),
|
||||
)
|
||||
|
||||
// DON'T RESUME A DOWNLOADED FILE lastState != VideoDownloadManager.DownloadType.IsDone &&
|
||||
if ((currentBytes * 100 / totalBytes) < 98) {
|
||||
list.add(
|
||||
if (lastState == VideoDownloadManager.DownloadType.IsDownloading)
|
||||
Pair(DOWNLOAD_ACTION_PAUSE_DOWNLOAD, R.string.popup_pause_download)
|
||||
else
|
||||
Pair(DOWNLOAD_ACTION_RESUME_DOWNLOAD, R.string.popup_resume_download)
|
||||
)
|
||||
}
|
||||
|
||||
it.popupMenuNoIcons(
|
||||
list
|
||||
) {
|
||||
_clickCallback?.invoke(DownloadClickEvent(itemId, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
downloadView.setOnLongClickListener {
|
||||
clickCallback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_LONG_CLICK, data))
|
||||
return@setOnLongClickListener true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ data class DownloadMetadata(
|
|||
val progressPercentage: Long
|
||||
get() = if (downloadedLength < 1024) 0 else maxOf(
|
||||
0,
|
||||
minOf(100, (downloadedLength * 100L) / totalLength)
|
||||
minOf(100, (downloadedLength * 100L) / (totalLength + 1))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -101,38 +101,41 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
|
|||
|
||||
open fun setProgress(downloadedBytes: Long, totalBytes: Long) {
|
||||
isZeroBytes = downloadedBytes == 0L
|
||||
val steps = 10000L
|
||||
progressBar.max = steps.toInt()
|
||||
// div by zero error and 1 byte off is ok impo
|
||||
val progress = (downloadedBytes * steps / (totalBytes + 1L)).toInt()
|
||||
progressBar.post {
|
||||
val steps = 10000L
|
||||
progressBar.max = steps.toInt()
|
||||
// div by zero error and 1 byte off is ok impo
|
||||
|
||||
val animation = ProgressBarAnimation(
|
||||
progressBar,
|
||||
progressBar.progress.toFloat(),
|
||||
progress.toFloat()
|
||||
).apply {
|
||||
fillAfter = true
|
||||
duration =
|
||||
if (progress > progressBar.progress) // we don't want to animate backward changes in progress
|
||||
100
|
||||
else
|
||||
0L
|
||||
}
|
||||
val progress = (downloadedBytes * steps / (totalBytes + 1L)).toInt()
|
||||
|
||||
if (isZeroBytes) {
|
||||
progressText?.isVisible = false
|
||||
} else {
|
||||
progressText?.apply {
|
||||
val currentMbString = Formatter.formatShortFileSize(context, downloadedBytes)
|
||||
val totalMbString = Formatter.formatShortFileSize(context, totalBytes)
|
||||
text =
|
||||
//if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else
|
||||
context?.getString(R.string.download_size_format)
|
||||
?.format(currentMbString, totalMbString)
|
||||
val animation = ProgressBarAnimation(
|
||||
progressBar,
|
||||
progressBar.progress.toFloat(),
|
||||
progress.toFloat()
|
||||
).apply {
|
||||
fillAfter = true
|
||||
duration =
|
||||
if (progress > progressBar.progress) // we don't want to animate backward changes in progress
|
||||
100
|
||||
else
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
progressBar.startAnimation(animation)
|
||||
if (isZeroBytes) {
|
||||
progressText?.isVisible = false
|
||||
} else {
|
||||
progressText?.apply {
|
||||
val currentMbString = Formatter.formatShortFileSize(context, downloadedBytes)
|
||||
val totalMbString = Formatter.formatShortFileSize(context, totalBytes)
|
||||
text =
|
||||
//if (isTextPercentage) "%d%%".format(setCurrentBytes * 100L / setTotalBytes) else
|
||||
context?.getString(R.string.download_size_format)
|
||||
?.format(currentMbString, totalMbString)
|
||||
}
|
||||
}
|
||||
|
||||
progressBar.startAnimation(animation)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadStatusEvent(data: Pair<Int, VideoDownloadManager.DownloadType>) {
|
||||
|
|
|
@ -21,14 +21,17 @@ class DownloadButton(context: Context, attributeSet: AttributeSet) :
|
|||
}
|
||||
|
||||
override fun setStatus(status: DownloadStatusTell?) {
|
||||
super.setStatus(status)
|
||||
val txt = when (status) {
|
||||
DownloadStatusTell.IsPaused -> R.string.download_paused
|
||||
DownloadStatusTell.IsDownloading -> R.string.downloading
|
||||
DownloadStatusTell.IsDone -> R.string.downloaded
|
||||
else -> R.string.download
|
||||
mainText?.post {
|
||||
val txt = when (status) {
|
||||
DownloadStatusTell.IsPaused -> R.string.download_paused
|
||||
DownloadStatusTell.IsDownloading -> R.string.downloading
|
||||
DownloadStatusTell.IsDone -> R.string.downloaded
|
||||
else -> R.string.download
|
||||
}
|
||||
mainText?.setText(txt)
|
||||
}
|
||||
mainText?.setText(txt)
|
||||
super.setStatus(status)
|
||||
|
||||
}
|
||||
|
||||
override fun setDefaultClickListener(
|
||||
|
|
|
@ -174,7 +174,7 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
|
|||
|
||||
currentMetaData.apply {
|
||||
// DON'T RESUME A DOWNLOADED FILE lastState != VideoDownloadManager.DownloadType.IsDone &&
|
||||
if ((downloadedLength * 100 / totalLength) < 98) {
|
||||
if (progressPercentage < 98) {
|
||||
list.add(
|
||||
if (status == VideoDownloadManager.DownloadType.IsDownloading)
|
||||
Pair(DOWNLOAD_ACTION_PAUSE_DOWNLOAD, R.string.popup_pause_download)
|
||||
|
@ -248,33 +248,34 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
|
|||
//progressBar.isVisible =
|
||||
// status != null && status != DownloadStatusTell.Complete && status != DownloadStatusTell.Error
|
||||
//progressBarBackground.isVisible = status != null && status != DownloadStatusTell.Complete
|
||||
val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading
|
||||
progressBarBackground.post {
|
||||
val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading
|
||||
if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) {
|
||||
val animation = AnimationUtils.loadAnimation(context, waitingAnimation)
|
||||
progressBarBackground.startAnimation(animation)
|
||||
} else {
|
||||
progressBarBackground.clearAnimation()
|
||||
}
|
||||
|
||||
if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) {
|
||||
val animation = AnimationUtils.loadAnimation(context, waitingAnimation)
|
||||
progressBarBackground.startAnimation(animation)
|
||||
} else {
|
||||
progressBarBackground.clearAnimation()
|
||||
val progressDrawable =
|
||||
if (status == DownloadStatusTell.IsDownloading && !isPreActive) activeOutline else nonActiveOutline
|
||||
|
||||
progressBarBackground.background =
|
||||
ContextCompat.getDrawable(context, progressDrawable)
|
||||
|
||||
val drawable = getDrawableFromStatus(status)
|
||||
statusView.setImageDrawable(drawable)
|
||||
val isDrawable = drawable != null
|
||||
|
||||
statusView.isVisible = isDrawable
|
||||
val hide = hideWhenIcon && isDrawable
|
||||
if (hide) {
|
||||
progressBar.clearAnimation()
|
||||
progressBarBackground.clearAnimation()
|
||||
}
|
||||
progressBarBackground.isGone = hide
|
||||
progressBar.isGone = hide
|
||||
}
|
||||
|
||||
val progressDrawable =
|
||||
if (status == DownloadStatusTell.IsDownloading && !isPreActive) activeOutline else nonActiveOutline
|
||||
|
||||
progressBarBackground.background =
|
||||
ContextCompat.getDrawable(context, progressDrawable)
|
||||
|
||||
val drawable = getDrawableFromStatus(status)
|
||||
statusView.setImageDrawable(drawable)
|
||||
val isDrawable = drawable != null
|
||||
|
||||
statusView.isVisible = isDrawable
|
||||
val hide = hideWhenIcon && isDrawable
|
||||
if (hide) {
|
||||
progressBar.clearAnimation()
|
||||
progressBarBackground.clearAnimation()
|
||||
}
|
||||
progressBarBackground.isGone = hide
|
||||
progressBar.isGone = hide
|
||||
}
|
||||
|
||||
override fun resetView() {
|
||||
|
|
|
@ -658,12 +658,14 @@ class HomeFragment : Fragment() {
|
|||
return@observeNullable
|
||||
}
|
||||
|
||||
bottomSheetDialog = activity?.loadHomepageList(item, expandCallback = {
|
||||
val (items, delete) = item
|
||||
|
||||
bottomSheetDialog = activity?.loadHomepageList(items, expandCallback = {
|
||||
homeViewModel.expandAndReturn(it)
|
||||
}, dismissCallback = {
|
||||
homeViewModel.popup(null)
|
||||
bottomSheetDialog = null
|
||||
})
|
||||
}, deleteCallback = delete)
|
||||
}
|
||||
|
||||
homeViewModel.reloadStored()
|
||||
|
|
|
@ -246,7 +246,7 @@ class HomeParentItemAdapterPreview(
|
|||
private val previewViewpagerText: ViewGroup =
|
||||
itemView.findViewById(R.id.home_preview_viewpager_text)
|
||||
|
||||
// private val previewHeader: FrameLayout = itemView.findViewById(R.id.home_preview)
|
||||
// private val previewHeader: FrameLayout = itemView.findViewById(R.id.home_preview)
|
||||
private var resumeHolder: View = itemView.findViewById(R.id.home_watch_holder)
|
||||
private var resumeRecyclerView: RecyclerView =
|
||||
itemView.findViewById(R.id.home_watch_child_recyclerview)
|
||||
|
@ -257,7 +257,7 @@ class HomeParentItemAdapterPreview(
|
|||
private var homeAccount: View? =
|
||||
itemView.findViewById(R.id.home_preview_switch_account)
|
||||
|
||||
private var topPadding : View? = itemView.findViewById(R.id.home_padding)
|
||||
private var topPadding: View? = itemView.findViewById(R.id.home_padding)
|
||||
|
||||
private val homeNonePadding: View = itemView.findViewById(R.id.home_none_padding)
|
||||
|
||||
|
@ -283,7 +283,11 @@ class HomeParentItemAdapterPreview(
|
|||
item.plot ?: ""
|
||||
|
||||
homePreviewText.text = item.name
|
||||
populateChips(homePreviewTags,item.tags ?: emptyList(), R.style.ChipFilledSemiTransparent)
|
||||
populateChips(
|
||||
homePreviewTags,
|
||||
item.tags ?: emptyList(),
|
||||
R.style.ChipFilledSemiTransparent
|
||||
)
|
||||
|
||||
homePreviewTags.isGone =
|
||||
item.tags.isNullOrEmpty()
|
||||
|
@ -413,7 +417,7 @@ class HomeParentItemAdapterPreview(
|
|||
Pair(itemView.findViewById(R.id.home_plan_to_watch_btt), WatchType.PLANTOWATCH),
|
||||
)
|
||||
|
||||
private val toggleListHolder : ChipGroup? = itemView.findViewById(R.id.home_type_holder)
|
||||
private val toggleListHolder: ChipGroup? = itemView.findViewById(R.id.home_type_holder)
|
||||
|
||||
init {
|
||||
previewViewpager.setPageTransformer(HomeScrollTransformer())
|
||||
|
@ -422,8 +426,14 @@ class HomeParentItemAdapterPreview(
|
|||
resumeRecyclerView.adapter = resumeAdapter
|
||||
bookmarkRecyclerView.adapter = bookmarkAdapter
|
||||
|
||||
resumeRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
|
||||
bookmarkRecyclerView.setLinearListLayout(nextLeft = R.id.nav_rail_view, nextRight = FOCUS_SELF)
|
||||
resumeRecyclerView.setLinearListLayout(
|
||||
nextLeft = R.id.nav_rail_view,
|
||||
nextRight = FOCUS_SELF
|
||||
)
|
||||
bookmarkRecyclerView.setLinearListLayout(
|
||||
nextLeft = R.id.nav_rail_view,
|
||||
nextRight = FOCUS_SELF
|
||||
)
|
||||
|
||||
fixPaddingStatusbarMargin(topPadding)
|
||||
|
||||
|
@ -539,7 +549,7 @@ class HomeParentItemAdapterPreview(
|
|||
resumeAdapter.updateList(resumeWatching)
|
||||
|
||||
if (binding is FragmentHomeHeadBinding) {
|
||||
binding.homeBookmarkParentItemTitle.setOnClickListener {
|
||||
binding.homeWatchParentItemTitle.setOnClickListener {
|
||||
viewModel.popup(
|
||||
HomeViewModel.ExpandableHomepageList(
|
||||
HomePageList(
|
||||
|
@ -547,7 +557,10 @@ class HomeParentItemAdapterPreview(
|
|||
resumeWatching,
|
||||
false
|
||||
), 1, false
|
||||
)
|
||||
),
|
||||
deleteCallback = {
|
||||
viewModel.deleteResumeWatching()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +585,9 @@ class HomeParentItemAdapterPreview(
|
|||
list,
|
||||
false
|
||||
), 1, false
|
||||
)
|
||||
), deleteCallback = {
|
||||
viewModel.deleteBookmarks(list)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadResult
|
|||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllBookmarkedData
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.deleteAllResumeStateIds
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllResumeStateIds
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
|
||||
|
@ -92,6 +94,21 @@ class HomeViewModel : ViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
fun deleteResumeWatching() {
|
||||
deleteAllResumeStateIds()
|
||||
loadResumeWatching()
|
||||
}
|
||||
|
||||
fun deleteBookmarks(list: List<SearchResponse>) {
|
||||
list.forEach { DataStoreHelper.deleteBookmarkedData(it.id) }
|
||||
loadStoredData()
|
||||
}
|
||||
|
||||
fun deleteBookmarks() {
|
||||
deleteAllBookmarkedData()
|
||||
loadStoredData()
|
||||
}
|
||||
|
||||
var repo: APIRepository? = null
|
||||
|
||||
private val _apiName = MutableLiveData<String>()
|
||||
|
@ -394,11 +411,14 @@ class HomeViewModel : ViewModel() {
|
|||
}
|
||||
|
||||
|
||||
private val _popup = MutableLiveData<ExpandableHomepageList?>(null)
|
||||
val popup: LiveData<ExpandableHomepageList?> = _popup
|
||||
private val _popup = MutableLiveData<Pair<ExpandableHomepageList, (() -> Unit)?>?>(null)
|
||||
val popup: LiveData<Pair<ExpandableHomepageList, (() -> Unit)?>?> = _popup
|
||||
|
||||
fun popup(list: ExpandableHomepageList?) {
|
||||
_popup.postValue(list)
|
||||
fun popup(list: ExpandableHomepageList?, deleteCallback: (() -> Unit)? = null) {
|
||||
if (list == null)
|
||||
_popup.postValue(null)
|
||||
else
|
||||
_popup.postValue(list to deleteCallback)
|
||||
}
|
||||
|
||||
private fun bookmarksUpdated(unused: Boolean) {
|
||||
|
@ -436,8 +456,7 @@ class HomeViewModel : ViewModel() {
|
|||
// do nothing
|
||||
}
|
||||
|
||||
fun reloadStored() {
|
||||
loadResumeWatching()
|
||||
fun loadStoredData() {
|
||||
val list = EnumSet.noneOf(WatchType::class.java)
|
||||
getKey<IntArray>(HOME_BOOKMARK_VALUE_LIST)?.map { WatchType.fromInternalId(it) }?.let {
|
||||
list.addAll(it)
|
||||
|
@ -445,6 +464,11 @@ class HomeViewModel : ViewModel() {
|
|||
loadStoredData(list)
|
||||
}
|
||||
|
||||
fun reloadStored() {
|
||||
loadResumeWatching()
|
||||
loadStoredData()
|
||||
}
|
||||
|
||||
fun click(load: LoadClickCallback) {
|
||||
loadResult(load.response.url, load.response.apiName, load.action)
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package com.lagradost.cloudstream3.ui.player
|
||||
|
||||
import android.content.ContentUris
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.KeyEvent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.lagradost.cloudstream3.CommonActivity
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.utils.ExtractorUri
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||
import com.lagradost.safefile.SafeFile
|
||||
|
||||
const val DTAG = "PlayerActivity"
|
||||
|
||||
|
@ -50,14 +51,17 @@ class DownloadedPlayerActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
private fun playUri(uri: Uri) {
|
||||
val name = UniFile.fromUri(this, uri).name
|
||||
val name = SafeFile.fromUri(this, uri)?.name()
|
||||
this.navigate(
|
||||
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
||||
DownloadFileGenerator(
|
||||
listOf(
|
||||
ExtractorUri(
|
||||
uri = uri,
|
||||
name = name ?: getString(R.string.downloaded_file)
|
||||
name = name ?: getString(R.string.downloaded_file),
|
||||
// well not the same as a normal id, but we take it as users may want to
|
||||
// play downloaded files and save the location
|
||||
id = kotlin.runCatching { ContentUris.parseId(uri) }.getOrNull()?.hashCode()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -2,9 +2,11 @@ package com.lagradost.cloudstream3.ui.player
|
|||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.media.AudioManager
|
||||
|
@ -16,6 +18,7 @@ import android.util.DisplayMetrics
|
|||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
|
@ -56,6 +59,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
|||
import com.lagradost.cloudstream3.utils.Vector2
|
||||
import kotlin.math.*
|
||||
|
||||
|
||||
const val MINIMUM_SEEK_TIME = 7000L // when swipe seeking
|
||||
const val MINIMUM_VERTICAL_SWIPE = 2.0f // in percentage
|
||||
const val MINIMUM_HORIZONTAL_SWIPE = 2.0f // in percentage
|
||||
|
@ -292,6 +296,36 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
player.getCurrentPreferredSubtitle() == null
|
||||
}
|
||||
|
||||
open fun lockOrientation(activity: Activity) {
|
||||
val display =
|
||||
(activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
|
||||
val rotation = display.rotation
|
||||
val currentOrientation = activity.resources.configuration.orientation
|
||||
var orientation = 0
|
||||
when (currentOrientation) {
|
||||
Configuration.ORIENTATION_LANDSCAPE -> orientation =
|
||||
if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90) ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
|
||||
|
||||
Configuration.ORIENTATION_SQUARE, Configuration.ORIENTATION_UNDEFINED, Configuration.ORIENTATION_PORTRAIT -> ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
//Configuration.ORIENTATION_PORTRAIT -> orientation =
|
||||
// if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_270) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT
|
||||
}
|
||||
activity.requestedOrientation = orientation
|
||||
}
|
||||
|
||||
private fun updateOrientation() {
|
||||
activity?.apply {
|
||||
if(lockRotation) {
|
||||
if(isLocked) {
|
||||
lockOrientation(this)
|
||||
}
|
||||
else {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun enterFullscreen() {
|
||||
if (isFullScreenPlayer) {
|
||||
activity?.hideSystemUI()
|
||||
|
@ -301,8 +335,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
activity?.window?.attributes = params
|
||||
}
|
||||
}
|
||||
if (lockRotation)
|
||||
activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|
||||
updateOrientation()
|
||||
}
|
||||
|
||||
protected fun exitFullscreen() {
|
||||
|
@ -561,6 +594,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
|
||||
isLocked = !isLocked
|
||||
updateOrientation()
|
||||
|
||||
if (isLocked && isShowing) {
|
||||
playerBinding?.playerHolder?.postDelayed({
|
||||
if (isLocked && isShowing) {
|
||||
|
|
|
@ -21,13 +21,11 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.preference.PreferenceManager
|
||||
import androidx.media3.common.Format.NO_VALUE
|
||||
import androidx.media3.common.MimeTypes
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
|
||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||
import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
|
||||
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
|
||||
import com.lagradost.cloudstream3.databinding.PlayerSelectSourceAndSubsBinding
|
||||
import com.lagradost.cloudstream3.databinding.PlayerSelectTracksBinding
|
||||
import com.lagradost.cloudstream3.mvvm.*
|
||||
|
@ -52,6 +50,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import com.lagradost.safefile.SafeFile
|
||||
import kotlinx.coroutines.Job
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
@ -135,7 +134,7 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
return durPos.position
|
||||
}
|
||||
|
||||
var currentVerifyLink: Job? = null
|
||||
private var currentVerifyLink: Job? = null
|
||||
|
||||
private fun loadExtractorJob(extractorLink: ExtractorLink?) {
|
||||
currentVerifyLink?.cancel()
|
||||
|
@ -520,15 +519,16 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
if (uri == null) return@normalSafeApiCall
|
||||
val ctx = context ?: AcraApplication.context ?: return@normalSafeApiCall
|
||||
// RW perms for the path
|
||||
val flags =
|
||||
ctx.contentResolver.takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
|
||||
ctx.contentResolver.takePersistableUriPermission(uri, flags)
|
||||
|
||||
val file = UniFile.fromUri(ctx, uri)
|
||||
println("Loaded subtitle file. Selected URI path: $uri - Name: ${file.name}")
|
||||
val file = SafeFile.fromUri(ctx, uri)
|
||||
val fileName = file?.name()
|
||||
println("Loaded subtitle file. Selected URI path: $uri - Name: $fileName")
|
||||
// DO NOT REMOVE THE FILE EXTENSION FROM NAME, IT'S NEEDED FOR MIME TYPES
|
||||
val name = file.name ?: uri.toString()
|
||||
val name = fileName ?: uri.toString()
|
||||
|
||||
val subtitleData = SubtitleData(
|
||||
name,
|
||||
|
|
|
@ -23,7 +23,6 @@ import androidx.core.view.isVisible
|
|||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.discord.panels.PanelsChildGestureRegionObserver
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
|
@ -62,8 +61,6 @@ import com.lagradost.cloudstream3.ui.search.SearchHelper
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.html
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isLtr
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isRtl
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
|
@ -81,8 +78,13 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
|||
import com.lagradost.cloudstream3.utils.VideoDownloadHelper
|
||||
|
||||
|
||||
open class ResultFragmentPhone : FullScreenPlayer(),
|
||||
PanelsChildGestureRegionObserver.GestureRegionsListener {
|
||||
open class ResultFragmentPhone : FullScreenPlayer() {
|
||||
private val gestureRegionsListener = object : PanelsChildGestureRegionObserver.GestureRegionsListener {
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {
|
||||
binding?.resultOverlappingPanels?.setChildGestureRegions(gestureRegions)
|
||||
}
|
||||
}
|
||||
|
||||
protected lateinit var viewModel: ResultViewModel2
|
||||
protected lateinit var syncModel: SyncViewModel
|
||||
|
||||
|
@ -211,14 +213,17 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
||||
//somehow this still leaks and I dont know why????
|
||||
// todo look at https://github.com/discord/OverlappingPanels/blob/70b4a7cf43c6771873b1e091029d332896d41a1a/sample_app/src/main/java/com/discord/sampleapp/MainActivity.kt
|
||||
PanelsChildGestureRegionObserver.Provider.get().let { obs ->
|
||||
resultBinding?.resultCastItems?.let {
|
||||
obs.unregister(it)
|
||||
}
|
||||
obs.removeGestureRegionsUpdateListener(this)
|
||||
|
||||
obs.removeGestureRegionsUpdateListener(gestureRegionsListener)
|
||||
}
|
||||
|
||||
updateUIEvent -= ::updateUI
|
||||
binding = null
|
||||
resultBinding = null
|
||||
|
@ -287,6 +292,8 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
it.colorFromAttribute(R.attr.primaryBlackBackground)
|
||||
}
|
||||
super.onResume()
|
||||
PanelsChildGestureRegionObserver.Provider.get()
|
||||
.addGestureRegionsUpdateListener(gestureRegionsListener)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
@ -323,7 +330,16 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
setUrl(storedData.url)
|
||||
syncModel.addFromUrl(storedData.url)
|
||||
val api = APIHolder.getApiFromNameNull(storedData.apiName)
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
|
||||
PanelsChildGestureRegionObserver.Provider.get().apply {
|
||||
resultBinding?.resultCastItems?.let {
|
||||
register(it)
|
||||
}
|
||||
addGestureRegionsUpdateListener(gestureRegionsListener)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ===== ===== =====
|
||||
|
||||
resultBinding?.apply {
|
||||
|
@ -374,9 +390,8 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
DownloadButtonSetup.handleDownloadClick(downloadClickEvent)
|
||||
}
|
||||
)
|
||||
resultCastItems.let {
|
||||
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
||||
}
|
||||
|
||||
|
||||
resultScroll.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
val dy = scrollY - oldScrollY
|
||||
if (dy > 0) { //check for scroll down
|
||||
|
@ -1055,11 +1070,7 @@ open class ResultFragmentPhone : FullScreenPlayer(),
|
|||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {
|
||||
binding?.resultOverlappingPanels?.setChildGestureRegions(gestureRegions)
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(gestureRegionsListener)
|
||||
}
|
||||
|
||||
private fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
|
|
|
@ -44,6 +44,8 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
|||
|
||||
private fun fixPlayerSize() {
|
||||
playerWidthHeight?.let { (w, h) ->
|
||||
if(w <= 0 || h <= 0) return@let
|
||||
|
||||
val orientation = context?.resources?.configuration?.orientation ?: return
|
||||
|
||||
val sw = if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
|
@ -118,9 +120,6 @@ open class ResultTrailerPlayer : ResultFragmentPhone(), IOnBackPressed {
|
|||
override fun onTracksInfoChanged() {}
|
||||
|
||||
override fun exitedPipMode() {}
|
||||
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {}
|
||||
|
||||
private fun updateFullscreen(fullscreen: Boolean) {
|
||||
isFullScreenPlayer = fullscreen
|
||||
lockRotation = fullscreen
|
||||
|
|
|
@ -591,12 +591,10 @@ class ResultViewModel2 : ViewModel() {
|
|||
link,
|
||||
"$fileName ${link.name}",
|
||||
folder,
|
||||
if (link.url.contains(".srt")) ".srt" else "vtt",
|
||||
if (link.url.contains(".srt")) "srt" else "vtt",
|
||||
false,
|
||||
null
|
||||
) {
|
||||
// no notification
|
||||
}
|
||||
null, createNotificationCallback = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -721,7 +719,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
)
|
||||
)
|
||||
}
|
||||
.map { ExtractorSubtitleLink(it.name, it.url, "") }
|
||||
.map { ExtractorSubtitleLink(it.name, it.url, "") }.take(3)
|
||||
.forEach { link ->
|
||||
val fileName = VideoDownloadManager.getFileName(context, meta)
|
||||
downloadSubtitle(context, link, fileName, folder)
|
||||
|
@ -1707,7 +1705,7 @@ class ResultViewModel2 : ViewModel() {
|
|||
else -> {
|
||||
if (response.type.isLiveStream())
|
||||
R.string.play_livestream_button
|
||||
else if (response.type.isMovieType()) // this wont break compatibility as you only need to override isMovieType
|
||||
else if (response.isMovie()) // this wont break compatibility as you only need to override isMovieType
|
||||
R.string.play_movie_button
|
||||
else null
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class SearchFragment : Fragment() {
|
|||
|
||||
fun newInstance(query: String): Bundle {
|
||||
return Bundle().apply {
|
||||
putString(SEARCH_QUERY, query)
|
||||
if(query.isNotBlank()) putString(SEARCH_QUERY, query)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ class SearchFragment : Fragment() {
|
|||
reloadRepos()
|
||||
|
||||
binding?.apply {
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>? =
|
||||
val adapter: RecyclerView.Adapter<RecyclerView.ViewHolder> =
|
||||
SearchAdapter(
|
||||
ArrayList(),
|
||||
searchAutofitResults,
|
||||
|
@ -530,11 +530,18 @@ class SearchFragment : Fragment() {
|
|||
searchMasterRecycler.layoutManager = GridLayoutManager(context, 1)
|
||||
|
||||
// Automatically search the specified query, this allows the app search to launch from intent
|
||||
arguments?.getString(SEARCH_QUERY)?.let { query ->
|
||||
var sq = arguments?.getString(SEARCH_QUERY) ?: savedInstanceState?.getString(SEARCH_QUERY)
|
||||
if(sq.isNullOrBlank()) {
|
||||
sq = MainActivity.nextSearchQuery
|
||||
}
|
||||
|
||||
sq?.let { query ->
|
||||
if (query.isBlank()) return@let
|
||||
mainSearch.setQuery(query, true)
|
||||
// Clear the query as to not make it request the same query every time the page is opened
|
||||
arguments?.putString(SEARCH_QUERY, null)
|
||||
arguments?.remove(SEARCH_QUERY)
|
||||
savedInstanceState?.remove(SEARCH_QUERY)
|
||||
MainActivity.nextSearchQuery = null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ package com.lagradost.cloudstream3.ui.settings
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
|
@ -13,7 +11,6 @@ import androidx.appcompat.app.AlertDialog
|
|||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.lagradost.cloudstream3.APIHolder.allProviders
|
||||
import com.lagradost.cloudstream3.AcraApplication
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
|
||||
|
@ -41,7 +38,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard
|
|||
import com.lagradost.cloudstream3.utils.USER_PROVIDER_API
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
|
||||
import java.io.File
|
||||
import com.lagradost.safefile.SafeFile
|
||||
|
||||
fun getCurrentLocale(context: Context): String {
|
||||
val res = context.resources
|
||||
|
@ -57,6 +54,8 @@ fun getCurrentLocale(context: Context): String {
|
|||
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes leave blank for auto
|
||||
val appLanguages = arrayListOf(
|
||||
/* begin language list */
|
||||
Triple("", "ajp", "ajp"),
|
||||
Triple("", "አማርኛ", "am"),
|
||||
Triple("", "العربية", "ar"),
|
||||
Triple("", "ars", "ars"),
|
||||
Triple("", "български", "bg"),
|
||||
|
@ -69,6 +68,7 @@ val appLanguages = arrayListOf(
|
|||
Triple("", "Esperanto", "eo"),
|
||||
Triple("", "español", "es"),
|
||||
Triple("", "فارسی", "fa"),
|
||||
Triple("", "fil", "fil"),
|
||||
Triple("", "français", "fr"),
|
||||
Triple("", "galego", "gl"),
|
||||
Triple("", "हिन्दी", "hi"),
|
||||
|
@ -84,6 +84,7 @@ val appLanguages = arrayListOf(
|
|||
Triple("", "македонски", "mk"),
|
||||
Triple("", "മലയാളം", "ml"),
|
||||
Triple("", "bahasa Melayu", "ms"),
|
||||
Triple("", "ဗမာစာ", "my"),
|
||||
Triple("", "Nederlands", "nl"),
|
||||
Triple("", "norsk nynorsk", "nn"),
|
||||
Triple("", "norsk bokmål", "no"),
|
||||
|
@ -97,6 +98,7 @@ val appLanguages = arrayListOf(
|
|||
Triple("", "Soomaaliga", "so"),
|
||||
Triple("", "svenska", "sv"),
|
||||
Triple("", "தமிழ்", "ta"),
|
||||
Triple("", "ትግርኛ", "ti"),
|
||||
Triple("", "Tagalog", "tl"),
|
||||
Triple("", "Türkçe", "tr"),
|
||||
Triple("", "українська", "uk"),
|
||||
|
@ -137,8 +139,9 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
|||
|
||||
context.contentResolver.takePersistableUriPermission(uri, flags)
|
||||
|
||||
val file = UniFile.fromUri(context, uri)
|
||||
println("Selected URI path: $uri - Full path: ${file.filePath}")
|
||||
val file = SafeFile.fromUri(context, uri)
|
||||
val filePath = file?.filePath()
|
||||
println("Selected URI path: $uri - Full path: $filePath")
|
||||
|
||||
// Stores the real URI using download_path_key
|
||||
// Important that the URI is stored instead of filepath due to permissions.
|
||||
|
@ -147,7 +150,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
|||
|
||||
// From URI -> File path
|
||||
// File path here is purely for cosmetic purposes in settings
|
||||
(file.filePath ?: uri.toString()).let {
|
||||
(filePath ?: uri.toString()).let {
|
||||
PreferenceManager.getDefaultSharedPreferences(context)
|
||||
.edit().putString(getString(R.string.download_path_pref), it).apply()
|
||||
}
|
||||
|
@ -304,25 +307,23 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
|||
}
|
||||
return@setOnPreferenceClickListener true
|
||||
}
|
||||
|
||||
fun getDownloadDirs(): List<String> {
|
||||
return normalSafeApiCall {
|
||||
val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath
|
||||
context?.let { ctx ->
|
||||
val defaultDir = VideoDownloadManager.getDefaultDir(ctx)?.filePath()
|
||||
|
||||
// app_name_download_path = Cloudstream and does not change depending on release.
|
||||
// DOES NOT WORK ON SCOPED STORAGE.
|
||||
val secondaryDir =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath +
|
||||
File.separator + resources.getString(R.string.app_name_download_path)
|
||||
val first = listOf(defaultDir, secondaryDir)
|
||||
(try {
|
||||
val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
|
||||
val first = listOf(defaultDir)
|
||||
(try {
|
||||
val currentDir = ctx.getBasePath().let { it.first?.filePath() ?: it.second }
|
||||
|
||||
(first +
|
||||
requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
|
||||
currentDir)
|
||||
} catch (e: Exception) {
|
||||
first
|
||||
}).filterNotNull().distinct()
|
||||
(first +
|
||||
ctx.getExternalFilesDirs("").mapNotNull { it.path } +
|
||||
currentDir)
|
||||
} catch (e: Exception) {
|
||||
first
|
||||
}).filterNotNull().distinct()
|
||||
}
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
|
@ -337,7 +338,7 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
|||
|
||||
val currentDir =
|
||||
settingsManager.getString(getString(R.string.download_path_pref), null)
|
||||
?: VideoDownloadManager.getDownloadDir().toString()
|
||||
?: context?.let { ctx -> VideoDownloadManager.getDefaultDir(ctx)?.filePath() }
|
||||
|
||||
activity?.showBottomDialog(
|
||||
dirs + listOf("Custom"),
|
||||
|
|
|
@ -116,13 +116,14 @@ class SettingsUpdates : PreferenceFragmentCompat() {
|
|||
null,
|
||||
"txt",
|
||||
false
|
||||
).fileStream
|
||||
fileStream?.writer()?.write(text)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
).openNew()
|
||||
fileStream.writer().write(text)
|
||||
dialog.dismissSafe(activity)
|
||||
} catch (t: Throwable) {
|
||||
logError(t)
|
||||
showToast(t.message)
|
||||
} finally {
|
||||
fileStream?.closeQuietly()
|
||||
dialog.dismissSafe(activity)
|
||||
}
|
||||
}
|
||||
binding.closeBtt.setOnClickListener {
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
|
@ -28,6 +25,7 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_T
|
|||
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY
|
||||
import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY
|
||||
import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY
|
||||
import com.lagradost.cloudstream3.ui.result.txt
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getDefaultSharedPrefs
|
||||
|
@ -36,9 +34,9 @@ import com.lagradost.cloudstream3.utils.DataStore.mapper
|
|||
import com.lagradost.cloudstream3.utils.DataStore.setKeyRaw
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.checkWrite
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.requestRW
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.isDownloadDir
|
||||
import java.io.IOException
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.setupStream
|
||||
import okhttp3.internal.closeQuietly
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintWriter
|
||||
import java.lang.System.currentTimeMillis
|
||||
import java.text.SimpleDateFormat
|
||||
|
@ -146,59 +144,25 @@ object BackupUtils {
|
|||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
fun FragmentActivity.backup() {
|
||||
fun FragmentActivity.backup() = ioSafe {
|
||||
var fileStream: OutputStream? = null
|
||||
var printStream: PrintWriter? = null
|
||||
try {
|
||||
if (!checkWrite()) {
|
||||
showToast(getString(R.string.backup_failed), Toast.LENGTH_LONG)
|
||||
showToast(R.string.backup_failed, Toast.LENGTH_LONG)
|
||||
requestRW()
|
||||
return
|
||||
return@ioSafe
|
||||
}
|
||||
|
||||
val subDir = getBasePath().first
|
||||
val date = SimpleDateFormat("yyyy_MM_dd_HH_mm").format(Date(currentTimeMillis()))
|
||||
val ext = "json"
|
||||
val ext = "txt"
|
||||
val displayName = "CS3_Backup_${date}"
|
||||
val backupFile = getBackup()
|
||||
val stream = setupStream(this@backup, displayName, null, ext, false)
|
||||
|
||||
val steam = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
||||
&& subDir?.isDownloadDir() == true
|
||||
) {
|
||||
val cr = this.contentResolver
|
||||
val contentUri =
|
||||
MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) // USE INSTEAD OF MediaStore.Downloads.EXTERNAL_CONTENT_URI
|
||||
//val currentMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
|
||||
|
||||
val newFile = ContentValues().apply {
|
||||
put(MediaStore.MediaColumns.DISPLAY_NAME, displayName)
|
||||
put(MediaStore.MediaColumns.TITLE, displayName)
|
||||
// While it a json file we store as txt because not
|
||||
// all file managers support mimetype json
|
||||
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")
|
||||
//put(MediaStore.MediaColumns.RELATIVE_PATH, folder)
|
||||
}
|
||||
|
||||
val newFileUri = cr.insert(
|
||||
contentUri,
|
||||
newFile
|
||||
) ?: throw IOException("Error creating file uri")
|
||||
cr.openOutputStream(newFileUri, "w")
|
||||
?: throw IOException("Error opening stream")
|
||||
} else {
|
||||
val fileName = "$displayName.$ext"
|
||||
val rFile = subDir?.findFile(fileName)
|
||||
if (rFile?.exists() == true) {
|
||||
rFile.delete()
|
||||
}
|
||||
val file =
|
||||
subDir?.createFile(fileName)
|
||||
?: throw IOException("Error creating file")
|
||||
if (!file.exists()) throw IOException("File does not exist")
|
||||
file.openOutputStream()
|
||||
}
|
||||
|
||||
val printStream = PrintWriter(steam)
|
||||
fileStream = stream.openNew()
|
||||
printStream = PrintWriter(fileStream)
|
||||
printStream.print(mapper.writeValueAsString(backupFile))
|
||||
printStream.close()
|
||||
|
||||
showToast(
|
||||
R.string.backup_success,
|
||||
|
@ -208,12 +172,15 @@ object BackupUtils {
|
|||
logError(e)
|
||||
try {
|
||||
showToast(
|
||||
getString(R.string.backup_failed_error_format).format(e.toString()),
|
||||
txt(R.string.backup_failed_error_format, e.toString()),
|
||||
Toast.LENGTH_LONG
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
} finally {
|
||||
printStream?.closeQuietly()
|
||||
fileStream?.closeQuietly()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -353,6 +353,12 @@ object DataStoreHelper {
|
|||
removeKeys(folder2)
|
||||
}
|
||||
|
||||
fun deleteBookmarkedData(id : Int?) {
|
||||
if (id == null) return
|
||||
removeKey("$currentAccount/$RESULT_WATCH_STATE", id.toString())
|
||||
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
||||
}
|
||||
|
||||
fun getAllResumeStateIds(): List<Int>? {
|
||||
val folder = "$currentAccount/$RESULT_RESUME_WATCHING"
|
||||
return getKeys(folder)?.mapNotNull {
|
||||
|
@ -519,12 +525,10 @@ object DataStoreHelper {
|
|||
|
||||
fun setResultWatchState(id: Int?, status: Int) {
|
||||
if (id == null) return
|
||||
val folder = "$currentAccount/$RESULT_WATCH_STATE"
|
||||
if (status == WatchType.NONE.internalId) {
|
||||
removeKey(folder, id.toString())
|
||||
removeKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
|
||||
deleteBookmarkedData(id)
|
||||
} else {
|
||||
setKey(folder, id.toString(), status)
|
||||
setKey("$currentAccount/$RESULT_WATCH_STATE", id.toString(), status)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import androidx.work.ForegroundInfo
|
|||
import androidx.work.WorkerParameters
|
||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.WORK_KEY_INFO
|
||||
|
@ -15,6 +16,7 @@ import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadCheck
|
|||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadEpisode
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadFromResume
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.downloadStatusEvent
|
||||
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadResumePackage
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
const val DOWNLOAD_CHECK = "DownloadCheck"
|
||||
|
@ -25,28 +27,32 @@ class DownloadFileWorkManager(val context: Context, private val workerParams: Wo
|
|||
override suspend fun doWork(): Result {
|
||||
val key = workerParams.inputData.getString("key")
|
||||
try {
|
||||
println("KEY $key")
|
||||
if (key == DOWNLOAD_CHECK) {
|
||||
downloadCheck(applicationContext, ::handleNotification)?.let {
|
||||
awaitDownload(it)
|
||||
}
|
||||
downloadCheck(applicationContext, ::handleNotification)
|
||||
} else if (key != null) {
|
||||
val info = applicationContext.getKey<VideoDownloadManager.DownloadInfo>(WORK_KEY_INFO, key)
|
||||
val info =
|
||||
applicationContext.getKey<VideoDownloadManager.DownloadInfo>(WORK_KEY_INFO, key)
|
||||
val pkg =
|
||||
applicationContext.getKey<VideoDownloadManager.DownloadResumePackage>(WORK_KEY_PACKAGE, key)
|
||||
if (info != null) {
|
||||
downloadEpisode(
|
||||
applicationContext,
|
||||
info.source,
|
||||
info.folder,
|
||||
info.ep,
|
||||
info.links,
|
||||
::handleNotification
|
||||
applicationContext.getKey<VideoDownloadManager.DownloadResumePackage>(
|
||||
WORK_KEY_PACKAGE,
|
||||
key
|
||||
)
|
||||
awaitDownload(info.ep.id)
|
||||
|
||||
if (info != null) {
|
||||
getDownloadResumePackage(applicationContext, info.ep.id)?.let { dpkg ->
|
||||
downloadFromResume(applicationContext, dpkg, ::handleNotification)
|
||||
} ?: run {
|
||||
downloadEpisode(
|
||||
applicationContext,
|
||||
info.source,
|
||||
info.folder,
|
||||
info.ep,
|
||||
info.links,
|
||||
::handleNotification
|
||||
)
|
||||
}
|
||||
} else if (pkg != null) {
|
||||
downloadFromResume(applicationContext, pkg, ::handleNotification)
|
||||
awaitDownload(pkg.item.ep.id)
|
||||
}
|
||||
removeKeys(key)
|
||||
}
|
||||
|
@ -73,6 +79,7 @@ class DownloadFileWorkManager(val context: Context, private val workerParams: Wo
|
|||
VideoDownloadManager.DownloadType.IsDone, VideoDownloadManager.DownloadType.IsFailed, VideoDownloadManager.DownloadType.IsStopped -> {
|
||||
isDone = true
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package com.lagradost.cloudstream3.utils
|
||||
|
||||
import com.lagradost.cloudstream3.ErrorLoadingException
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.delay
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
/** backwards api surface */
|
||||
class M3u8Helper {
|
||||
companion object {
|
||||
private val generator = M3u8Helper()
|
||||
suspend fun generateM3u8(
|
||||
source: String,
|
||||
streamUrl: String,
|
||||
|
@ -20,34 +20,59 @@ class M3u8Helper {
|
|||
headers: Map<String, String> = mapOf(),
|
||||
name: String = source
|
||||
): List<ExtractorLink> {
|
||||
return generator.m3u8Generation(
|
||||
M3u8Stream(
|
||||
streamUrl = streamUrl,
|
||||
quality = quality,
|
||||
headers = headers,
|
||||
), null
|
||||
)
|
||||
.map { stream ->
|
||||
ExtractorLink(
|
||||
source,
|
||||
name = name,
|
||||
stream.streamUrl,
|
||||
referer,
|
||||
stream.quality ?: Qualities.Unknown.value,
|
||||
true,
|
||||
stream.headers,
|
||||
)
|
||||
}
|
||||
return M3u8Helper2.generateM3u8(source, streamUrl, referer, quality, headers, name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
data class M3u8Stream(
|
||||
val streamUrl: String,
|
||||
val quality: Int? = null,
|
||||
val headers: Map<String, String> = mapOf()
|
||||
)
|
||||
|
||||
suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean? = true): List<M3u8Stream> {
|
||||
return M3u8Helper2.m3u8Generation(m3u8, returnThis)
|
||||
}
|
||||
}
|
||||
|
||||
object M3u8Helper2 {
|
||||
suspend fun generateM3u8(
|
||||
source: String,
|
||||
streamUrl: String,
|
||||
referer: String,
|
||||
quality: Int? = null,
|
||||
headers: Map<String, String> = mapOf(),
|
||||
name: String = source
|
||||
): List<ExtractorLink> {
|
||||
return m3u8Generation(
|
||||
M3u8Helper.M3u8Stream(
|
||||
streamUrl = streamUrl,
|
||||
quality = quality,
|
||||
headers = headers,
|
||||
), null
|
||||
)
|
||||
.map { stream ->
|
||||
ExtractorLink(
|
||||
source,
|
||||
name = name,
|
||||
stream.streamUrl,
|
||||
referer,
|
||||
stream.quality ?: Qualities.Unknown.value,
|
||||
true,
|
||||
stream.headers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val ENCRYPTION_DETECTION_REGEX = Regex("#EXT-X-KEY:METHOD=([^,]+),")
|
||||
private val ENCRYPTION_URL_IV_REGEX =
|
||||
Regex("#EXT-X-KEY:METHOD=([^,]+),URI=\"([^\"]+)\"(?:,IV=(.*))?")
|
||||
private val QUALITY_REGEX =
|
||||
Regex("""#EXT-X-STREAM-INF:(?:(?:.*?(?:RESOLUTION=\d+x(\d+)).*?\s+(.*))|(?:.*?\s+(.*)))""")
|
||||
private val TS_EXTENSION_REGEX =
|
||||
Regex("""(.*\.ts.*|.*\.jpg.*)""") //.jpg here 'case vizcloud uses .jpg instead of .ts
|
||||
Regex("""#EXTINF:.*\n(.+?\n)""") // fuck it we ball, who cares about the type anyways
|
||||
//Regex("""(.*\.(ts|jpg|html).*)""") //.jpg here 'case vizcloud uses .jpg instead of .ts
|
||||
|
||||
private fun absoluteExtensionDetermination(url: String): String? {
|
||||
val split = url.split("/")
|
||||
|
@ -64,21 +89,17 @@ class M3u8Helper {
|
|||
}
|
||||
}
|
||||
|
||||
private val defaultIvGen = sequence {
|
||||
var initial = 1
|
||||
private fun defaultIv(index: Int) : ByteArray {
|
||||
return toBytes16Big(index+1)
|
||||
}
|
||||
|
||||
while (true) {
|
||||
yield(toBytes16Big(initial))
|
||||
++initial
|
||||
}
|
||||
}.iterator()
|
||||
|
||||
private fun getDecrypter(
|
||||
fun getDecrypted(
|
||||
secretKey: ByteArray,
|
||||
data: ByteArray,
|
||||
iv: ByteArray = "".toByteArray()
|
||||
iv: ByteArray = byteArrayOf(),
|
||||
index : Int,
|
||||
): ByteArray {
|
||||
val ivKey = if (iv.isEmpty()) defaultIvGen.next() else iv
|
||||
val ivKey = if (iv.isEmpty()) defaultIv(index) else iv
|
||||
val c = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
val skSpec = SecretKeySpec(secretKey, "AES")
|
||||
val ivSpec = IvParameterSpec(ivKey)
|
||||
|
@ -91,13 +112,8 @@ class M3u8Helper {
|
|||
return st != null && (st.value.isNotEmpty() || st.destructured.component1() != "NONE")
|
||||
}
|
||||
|
||||
data class M3u8Stream(
|
||||
val streamUrl: String,
|
||||
val quality: Int? = null,
|
||||
val headers: Map<String, String> = mapOf()
|
||||
)
|
||||
|
||||
private fun selectBest(qualities: List<M3u8Stream>): M3u8Stream? {
|
||||
private fun selectBest(qualities: List<M3u8Helper.M3u8Stream>): M3u8Helper.M3u8Stream? {
|
||||
val result = qualities.sortedBy {
|
||||
if (it.quality != null && it.quality <= 1080) it.quality else 0
|
||||
}.filter {
|
||||
|
@ -113,19 +129,16 @@ class M3u8Helper {
|
|||
}
|
||||
|
||||
private fun isNotCompleteUrl(url: String): Boolean {
|
||||
return !url.contains("https://") && !url.contains("http://")
|
||||
return !url.startsWith("https://") && !url.startsWith("http://")
|
||||
}
|
||||
|
||||
suspend fun m3u8Generation(m3u8: M3u8Stream, returnThis: Boolean? = true): List<M3u8Stream> {
|
||||
// return listOf(m3u8)
|
||||
val list = mutableListOf<M3u8Stream>()
|
||||
suspend fun m3u8Generation(m3u8: M3u8Helper.M3u8Stream, returnThis: Boolean? = true): List<M3u8Helper.M3u8Stream> {
|
||||
val list = mutableListOf<M3u8Helper.M3u8Stream>()
|
||||
|
||||
val m3u8Parent = getParentLink(m3u8.streamUrl)
|
||||
val response = app.get(m3u8.streamUrl, headers = m3u8.headers, verify = false).text
|
||||
|
||||
// var hasAnyContent = false
|
||||
for (match in QUALITY_REGEX.findAll(response)) {
|
||||
// hasAnyContent = true
|
||||
var (quality, m3u8Link, m3u8Link2) = match.destructured
|
||||
if (m3u8Link.isEmpty()) m3u8Link = m3u8Link2
|
||||
if (absoluteExtensionDetermination(m3u8Link) == "m3u8") {
|
||||
|
@ -136,21 +149,21 @@ class M3u8Helper {
|
|||
println(m3u8.streamUrl)
|
||||
}
|
||||
list += m3u8Generation(
|
||||
M3u8Stream(
|
||||
M3u8Helper.M3u8Stream(
|
||||
m3u8Link,
|
||||
quality.toIntOrNull(),
|
||||
m3u8.headers
|
||||
), false
|
||||
)
|
||||
}
|
||||
list += M3u8Stream(
|
||||
list += M3u8Helper.M3u8Stream(
|
||||
m3u8Link,
|
||||
quality.toIntOrNull(),
|
||||
m3u8.headers
|
||||
)
|
||||
}
|
||||
if (returnThis != false) {
|
||||
list += M3u8Stream(
|
||||
list += M3u8Helper.M3u8Stream(
|
||||
m3u8.streamUrl,
|
||||
Qualities.Unknown.value,
|
||||
m3u8.headers
|
||||
|
@ -160,113 +173,134 @@ class M3u8Helper {
|
|||
return list
|
||||
}
|
||||
|
||||
data class LazyHlsDownloadData(
|
||||
private val encryptionData: ByteArray,
|
||||
private val encryptionIv: ByteArray,
|
||||
private val isEncrypted: Boolean,
|
||||
private val allTsLinks: List<String>,
|
||||
private val relativeUrl: String,
|
||||
private val headers: Map<String, String>,
|
||||
) {
|
||||
val size get() = allTsLinks.size
|
||||
|
||||
data class HlsDownloadData(
|
||||
val bytes: ByteArray,
|
||||
val currentIndex: Int,
|
||||
val totalTs: Int,
|
||||
val errored: Boolean = false
|
||||
)
|
||||
suspend fun resolveLinkWhileSafe(
|
||||
index: Int,
|
||||
tries: Int = 3,
|
||||
failDelay: Long = 3000,
|
||||
condition : (() -> Boolean)
|
||||
): ByteArray? {
|
||||
for (i in 0 until tries) {
|
||||
if(!condition()) return null
|
||||
|
||||
suspend fun hlsYield(
|
||||
qualities: List<M3u8Stream>,
|
||||
startIndex: Int = 0
|
||||
): Iterator<HlsDownloadData> {
|
||||
if (qualities.isEmpty()) return listOf(
|
||||
HlsDownloadData(
|
||||
byteArrayOf(),
|
||||
1,
|
||||
1,
|
||||
true
|
||||
)
|
||||
).iterator()
|
||||
|
||||
var selected = selectBest(qualities)
|
||||
if (selected == null) {
|
||||
selected = qualities[0]
|
||||
try {
|
||||
val out = resolveLink(index)
|
||||
return if(condition()) out else null
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return null
|
||||
} catch (e : CancellationException) {
|
||||
return null
|
||||
} catch (t: Throwable) {
|
||||
delay(failDelay)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun resolveLinkSafe(
|
||||
index: Int,
|
||||
tries: Int = 3,
|
||||
failDelay: Long = 3000
|
||||
): ByteArray? {
|
||||
for (i in 0 until tries) {
|
||||
try {
|
||||
return resolveLink(index)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
return null
|
||||
} catch (e : CancellationException) {
|
||||
return null
|
||||
} catch (t: Throwable) {
|
||||
delay(failDelay)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Throws
|
||||
suspend fun resolveLink(index: Int): ByteArray {
|
||||
if (index < 0 || index >= size) throw IllegalArgumentException("index must be in the bounds of the ts")
|
||||
val url = allTsLinks[index]
|
||||
|
||||
val tsResponse = app.get(url, headers = headers, verify = false)
|
||||
val tsData = tsResponse.body.bytes()
|
||||
if (tsData.isEmpty()) throw ErrorLoadingException("no data")
|
||||
|
||||
return if (isEncrypted) {
|
||||
getDecrypted(encryptionData, tsData, encryptionIv, index)
|
||||
} else {
|
||||
tsData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws
|
||||
suspend fun hslLazy(
|
||||
qualities: List<M3u8Helper.M3u8Stream>
|
||||
): LazyHlsDownloadData {
|
||||
if (qualities.isEmpty()) throw IllegalArgumentException("qualities must be non empty")
|
||||
val selected = selectBest(qualities) ?: qualities.first()
|
||||
val headers = selected.headers
|
||||
|
||||
val streams = qualities.map { m3u8Generation(it, false) }.flatten()
|
||||
//val sslVerification = if (headers.containsKey("ssl_verification")) headers["ssl_verification"].toBoolean() else true
|
||||
|
||||
// this selects the best quality of the qualities offered,
|
||||
// due to the recursive nature of m3u8, we only go 2 depth
|
||||
val secondSelection = selectBest(streams.ifEmpty { listOf(selected) })
|
||||
if (secondSelection != null) {
|
||||
val m3u8Response =
|
||||
runBlocking {
|
||||
app.get(
|
||||
secondSelection.streamUrl,
|
||||
headers = headers,
|
||||
verify = false
|
||||
).text
|
||||
}
|
||||
?: throw IllegalArgumentException("qualities has no streams")
|
||||
|
||||
var encryptionUri: String?
|
||||
var encryptionIv = byteArrayOf()
|
||||
var encryptionData = byteArrayOf()
|
||||
val m3u8Response =
|
||||
app.get(
|
||||
secondSelection.streamUrl,
|
||||
headers = headers,
|
||||
verify = false
|
||||
).text
|
||||
|
||||
val encryptionState = isEncrypted(m3u8Response)
|
||||
// encryption, this is because crunchy uses it
|
||||
var encryptionIv = byteArrayOf()
|
||||
var encryptionData = byteArrayOf()
|
||||
|
||||
if (encryptionState) {
|
||||
val match =
|
||||
ENCRYPTION_URL_IV_REGEX.find(m3u8Response)!!.destructured // its safe to assume that its not going to be null
|
||||
encryptionUri = match.component2()
|
||||
val encryptionState = isEncrypted(m3u8Response)
|
||||
|
||||
if (isNotCompleteUrl(encryptionUri)) {
|
||||
encryptionUri = "${getParentLink(secondSelection.streamUrl)}/$encryptionUri"
|
||||
}
|
||||
if (encryptionState) {
|
||||
// its safe to assume that its not going to be null
|
||||
val match =
|
||||
ENCRYPTION_URL_IV_REGEX.find(m3u8Response)!!.groupValues
|
||||
|
||||
encryptionIv = match.component3().toByteArray()
|
||||
val encryptionKeyResponse =
|
||||
runBlocking { app.get(encryptionUri, headers = headers, verify = false) }
|
||||
encryptionData = encryptionKeyResponse.body?.bytes() ?: byteArrayOf()
|
||||
var encryptionUri = match[2]
|
||||
|
||||
if (isNotCompleteUrl(encryptionUri)) {
|
||||
encryptionUri = "${getParentLink(secondSelection.streamUrl)}/$encryptionUri"
|
||||
}
|
||||
|
||||
val allTs = TS_EXTENSION_REGEX.findAll(m3u8Response)
|
||||
val allTsList = allTs.toList()
|
||||
val totalTs = allTsList.size
|
||||
if (totalTs == 0) {
|
||||
return listOf(HlsDownloadData(byteArrayOf(), 1, 1, true)).iterator()
|
||||
}
|
||||
var lastYield = 0
|
||||
|
||||
val relativeUrl = getParentLink(secondSelection.streamUrl)
|
||||
var retries = 0
|
||||
val tsByteGen = sequence {
|
||||
loop@ for ((index, ts) in allTs.withIndex()) {
|
||||
val url = if (
|
||||
isNotCompleteUrl(ts.destructured.component1())
|
||||
) "$relativeUrl/${ts.destructured.component1()}" else ts.destructured.component1()
|
||||
val c = index + 1 + startIndex
|
||||
|
||||
while (lastYield != c) {
|
||||
try {
|
||||
val tsResponse =
|
||||
runBlocking { app.get(url, headers = headers, verify = false) }
|
||||
var tsData = tsResponse.body?.bytes() ?: byteArrayOf()
|
||||
|
||||
if (encryptionState) {
|
||||
tsData = getDecrypter(encryptionData, tsData, encryptionIv)
|
||||
yield(HlsDownloadData(tsData, c, totalTs))
|
||||
lastYield = c
|
||||
break
|
||||
}
|
||||
yield(HlsDownloadData(tsData, c, totalTs))
|
||||
lastYield = c
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
if (retries == 3) {
|
||||
yield(HlsDownloadData(byteArrayOf(), c, totalTs, true))
|
||||
break@loop
|
||||
}
|
||||
++retries
|
||||
Thread.sleep(2_000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tsByteGen.iterator()
|
||||
encryptionIv = match[3].toByteArray()
|
||||
val encryptionKeyResponse = app.get(encryptionUri, headers = headers, verify = false)
|
||||
encryptionData = encryptionKeyResponse.body.bytes()
|
||||
}
|
||||
return listOf(HlsDownloadData(byteArrayOf(), 1, 1, true)).iterator()
|
||||
val relativeUrl = getParentLink(secondSelection.streamUrl)
|
||||
val allTsList = TS_EXTENSION_REGEX.findAll(m3u8Response + "\n").map { ts ->
|
||||
val value = ts.groupValues[1]
|
||||
if (isNotCompleteUrl(value)) {
|
||||
"$relativeUrl/${value}"
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}.toList()
|
||||
if (allTsList.isEmpty()) throw IllegalArgumentException("ts must be non empty")
|
||||
|
||||
return LazyHlsDownloadData(
|
||||
encryptionData = encryptionData,
|
||||
encryptionIv = encryptionIv,
|
||||
isEncrypted = encryptionState,
|
||||
allTsLinks = allTsList,
|
||||
relativeUrl = relativeUrl,
|
||||
headers = headers
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,18 @@ package com.lagradost.cloudstream3.utils
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.ui.download.EasyDownloadButton
|
||||
|
||||
object VideoDownloadHelper {
|
||||
data class DownloadEpisodeCached(
|
||||
@JsonProperty("name") val name: String?,
|
||||
@JsonProperty("poster") val poster: String?,
|
||||
@JsonProperty("episode") val episode: Int,
|
||||
@JsonProperty("season") val season: Int?,
|
||||
@JsonProperty("id") override val id: Int,
|
||||
@JsonProperty("id") val id: Int,
|
||||
@JsonProperty("parentId") val parentId: Int,
|
||||
@JsonProperty("rating") val rating: Int?,
|
||||
@JsonProperty("description") val description: String?,
|
||||
@JsonProperty("cacheTime") val cacheTime: Long,
|
||||
) : EasyDownloadButton.IMinimumData
|
||||
)
|
||||
|
||||
data class DownloadHeaderCached(
|
||||
@JsonProperty("apiName") val apiName: String,
|
||||
|
|
File diff suppressed because it is too large
Load diff
10
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
10
app/src/main/res/drawable/baseline_stop_24.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/white"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,6h12v12H6z" />
|
||||
</vector>
|
|
@ -61,7 +61,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="-50dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/account"
|
||||
android:nextFocusLeft="@id/home_search"
|
||||
android:padding="10dp"
|
||||
|
|
|
@ -476,7 +476,7 @@
|
|||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
tools:listitem="@layout/cast_item"
|
||||
tools:visibility="gone" />
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/result_vpn"
|
||||
|
|
2
app/src/main/res/values-ajp/strings.xml
Normal file
2
app/src/main/res/values-ajp/strings.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources/>
|
5
app/src/main/res/values-am/strings.xml
Normal file
5
app/src/main/res/values-am/strings.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s ክፍል %d</string>
|
||||
<string name="cast_format" formatted="true">ተዋናዮች: %s</string>
|
||||
</resources>
|
|
@ -584,4 +584,5 @@
|
|||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="no_plugins_found_error">لا توجد اضافة في المستودع</string>
|
||||
<string name="no_repository_found_error">المستودع لم يتم العثور عليه، تحقق من العنوان اوجرب شبكة افتراضية خاصة(vpn)</string>
|
||||
<string name="already_voted">لقد صوتت بالفعل</string>
|
||||
</resources>
|
||||
|
|
|
@ -1,2 +1,203 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources/>
|
||||
<resources>
|
||||
<string name="search_poster_img_des">لافتة</string>
|
||||
<string name="home_change_provider_img_des">تغيير مزود</string>
|
||||
<string name="downloading">جارى التحميل</string>
|
||||
<string name="cast_format" formatted="true">بث%s</string>
|
||||
<string name="filler" formatted="true">ملء</string>
|
||||
<string name="skip_loading">تخطي التحميل</string>
|
||||
<string name="loading">تحميل…</string>
|
||||
<string name="pick_subtitle">ترجمات</string>
|
||||
<string name="reload_error">إعادة محاولة الاتصال …</string>
|
||||
<string name="app_dub_sub_episode_text_format" formatted="true">%sييبي%d</string>
|
||||
<string name="next_episode_format" formatted="true">الحلقة%dسيتم نشرها في</string>
|
||||
<string name="next_episode_time_day_format" formatted="true">%dي%dس%dد</string>
|
||||
<string name="next_episode_time_hour_format" formatted="true">%dس%dد</string>
|
||||
<string name="next_episode_time_min_format" formatted="true">%dد</string>
|
||||
<string name="episode_poster_img_des">لافتة الحلقة</string>
|
||||
<string name="home_main_poster_img_des">اللافتة الاساسية</string>
|
||||
<string name="go_back_img_des">اذهب للخالف</string>
|
||||
<string name="preview_background_img_des">معاينة الخلفية</string>
|
||||
<string name="player_speed_text_format" formatted="true">سرعة(%.2fx)</string>
|
||||
<string name="play_with_app_name">فتح مع كلاودستريم</string>
|
||||
<string name="title_home">الصفحة الاساسية</string>
|
||||
<string name="search_hint_site" formatted="true">...%sابحث</string>
|
||||
<string name="no_data">لايوجد بيانات</string>
|
||||
<string name="episode_more_options_des">المزيد من الخيارات</string>
|
||||
<string name="result_open_in_browser">فتح في المتصفح</string>
|
||||
<string name="browser">المتصفح</string>
|
||||
<string name="play_movie_button">شاهد الفلم</string>
|
||||
<string name="play_torrent_button">دفق التورنت</string>
|
||||
<string name="download_started">بدأ التنزيل</string>
|
||||
<string name="home_next_random_img_des">عشوائي قادم</string>
|
||||
<string name="play_trailer_button">تشغيل المقطع الدعائي</string>
|
||||
<string name="result_tags">الأنواع</string>
|
||||
<string name="download_paused">توقف التنزيل</string>
|
||||
<string name="type_plan_to_watch">خطط للمشاهدة</string>
|
||||
<string name="type_none">لا يوجد</string>
|
||||
<string name="type_re_watching">إعادة المشاهدة</string>
|
||||
<string name="new_update_format" formatted="true">!تم العثور على تحديث جديد
|
||||
\n%s->%s</string>
|
||||
<string name="rated_format" formatted="true">%.1f:قدر</string>
|
||||
<string name="duration_format" formatted="true">%dاقل</string>
|
||||
<string name="app_name">كلاودستريم</string>
|
||||
<string name="title_search">بحث</string>
|
||||
<string name="title_downloads">التحميلات</string>
|
||||
<string name="title_settings">اعدادات</string>
|
||||
<string name="search_hint">...بحث</string>
|
||||
<string name="next_episode">الحلقة القادمة</string>
|
||||
<string name="result_share">شارك</string>
|
||||
<string name="type_watching">مشاهدة</string>
|
||||
<string name="type_on_hold">في التوقف</string>
|
||||
<string name="type_completed">مكتمل</string>
|
||||
<string name="type_dropped">توقف</string>
|
||||
<string name="play_livestream_button">تشغيل البث المباشر</string>
|
||||
<string name="pick_source">مصادر</string>
|
||||
<string name="play_episode">تشغيل الحلقة</string>
|
||||
<string name="download_canceled">تم إلغاء التنزيل</string>
|
||||
<string name="download_done">تم التنزيل</string>
|
||||
<string name="downloaded">تنززل</string>
|
||||
<string name="download">تحميل</string>
|
||||
<string name="go_back">عُد</string>
|
||||
<string name="download_failed">التحميل فشل</string>
|
||||
<string name="use_system_brightness_settings_des">استخدم سطوع النظام في مشغل التطبيق بدلاً من التراكب الداكن</string>
|
||||
<string name="restore_success">تم تحميل ملف النسخ الاحتياطي</string>
|
||||
<string name="advanced_search">البحث المتقدم</string>
|
||||
<string name="player_size_settings_des">إزالة الحدود السوداء</string>
|
||||
<string name="player_subtitles_settings">ترجمات</string>
|
||||
<string name="eigengraumode_settings_des">يضيف خيار السرعة في المشغل</string>
|
||||
<string name="double_tap_to_seek_settings">انقر نقرا مزدوجا للبحث</string>
|
||||
<string name="double_tap_to_pause_settings">انقر نقرًا مزدوجًا للإيقاف المؤقت</string>
|
||||
<string name="double_tap_to_seek_amount_settings">اللاعب يبحث عن المبلغ (بالثواني)</string>
|
||||
<string name="swipe_to_seek_settings_des">اسحب من جانب إلى آخر للتحكم بموقعك في الفيديو</string>
|
||||
<string name="autoplay_next_settings_des">ابدأ الحلقة التالية عندما تنتهي الحلقة الحالية</string>
|
||||
<string name="use_system_brightness_settings">استخدام سطوع النظام</string>
|
||||
<string name="episode_sync_settings">تحديث مراقبة التقدم</string>
|
||||
<string name="episode_sync_settings_des">قم بمزامنة تقدم الحلقة الحالية تلقائيًا</string>
|
||||
<string name="swipe_to_change_settings">اسحب لتغيير الإعدادات</string>
|
||||
<string name="restore_settings">استعادة البيانات من النسخة الاحتياطية</string>
|
||||
<string name="restore_failed_format" formatted="true">فشل في استعادة البيانات من الملف %s</string>
|
||||
<string name="double_tap_to_seek_settings_des">انقر مرتين على الجانب الأيمن أو الأيسر للبحث للأمام أو للخلف</string>
|
||||
<string name="backup_success">البيانات المخزنة</string>
|
||||
<string name="double_tap_to_pause_settings_des">اضغط مرتين في المنتصف للتوقف مؤقتًا</string>
|
||||
<string name="backup_failed">أذونات التخزين مفقودة. حاول مرة اخرى.</string>
|
||||
<string name="backup_failed_error_format">حدث خطأ أثناء النسخ الاحتياطي %s</string>
|
||||
<string name="search">بحث</string>
|
||||
<string name="library">مكتبة</string>
|
||||
<string name="settings_info">معلومات</string>
|
||||
<string name="category_updates">التحديثات والنسخ الاحتياطي</string>
|
||||
<string name="advanced_search_des">يعطيك نتائج البحث مفصولة حسب المزود</string>
|
||||
<string name="bug_report_settings_off">يرسل فقط البيانات عن الأعطال</string>
|
||||
<string name="show_trailers_settings">عرض المقطورات</string>
|
||||
<string name="kitsu_settings">عرض الملصقات من كيتسو</string>
|
||||
<string name="category_account">حسابات</string>
|
||||
<string name="bug_report_settings_on">لا يرسل أي بيانات</string>
|
||||
<string name="show_fillers_settings">عرض حلقة حشو للأنمي</string>
|
||||
<string name="pref_filter_search_quality">إخفاء جودة الفيديو المحددة في نتائج البحث</string>
|
||||
<string name="automatic_plugin_updates">تحديثات البرنامج المساعد التلقائي</string>
|
||||
<string name="updates_settings_des">البحث تلقائيًا عن التحديثات الجديدة بعد بدء تشغيل التطبيق.</string>
|
||||
<string name="uprereleases_settings">التحديث إلى الإصداراالمسبق</string>
|
||||
<string name="automatic_plugin_download">تنزيل المكونات الإضافية تلقائيًا</string>
|
||||
<string name="redo_setup_process">إعادة عملية الإعداد</string>
|
||||
<string name="uprereleases_settings_des">ابحث عن تحديثات الإصدار التجريبي بدلاً من الإصدارات الكاملة فقط</string>
|
||||
<string name="automatic_plugin_download_mode_title">حدد الوضع لتصفية تنزيل المكونات الإضافية</string>
|
||||
<string name="automatic_plugin_download_summary">قم تلقائيًا بتثبيت جميع المكونات الإضافية التي لم يتم تثبيتها بعد من المستودعات المضافة.</string>
|
||||
<string name="chromecast_subtitles_settings_des">إعدادات ترجمات كرومكاست</string>
|
||||
<string name="eigengraumode_settings">وضع إيجينجرافي</string>
|
||||
<string name="swipe_to_seek_settings">انتقد للبحث</string>
|
||||
<string name="backup_settings">نسخ إحتياطي للبيانات</string>
|
||||
<string name="updates_settings">إظهار تحديثات التطبيق</string>
|
||||
<string name="player_subtitles_settings_des">إعدادات ترجمات المشغل</string>
|
||||
<string name="chromecast_subtitles_settings">ترجمات كرومكاست</string>
|
||||
<string name="swipe_to_change_settings_des">قم بالتمرير لأعلى أو لأسفل على الجانب الأيسر أو الأيمن لتغيير السطوع أو مستوى الصوت</string>
|
||||
<string name="autoplay_next_settings">التشغيل التلقائي للحلقة القادمة</string>
|
||||
<string name="lightnovel">تطبيق رواية خفيفة من نفس المطورين</string>
|
||||
<string name="benene">أعط بينيني للمطورين</string>
|
||||
<string name="github">جيتهب</string>
|
||||
<string name="anim">تطبيق انيمي من نفس المطورين</string>
|
||||
<string name="app_language">لغة التطبيق</string>
|
||||
<string name="discord">انضم إلى الديسكورد</string>
|
||||
<string name="benene_des">بنيني معطا</string>
|
||||
<string name="apk_installer_settings_des">بعض الهواتف لا تدعم مثبت الحزمة الجديد. جرب الخيار القديم إذا لم يتم تثبيت التحديثات.</string>
|
||||
<string name="apk_installer_settings">مثبت تتبيق</string>
|
||||
<string name="test_passed">اجتاز</string>
|
||||
<string name="episodes">الحلقات</string>
|
||||
<string name="season">موسم</string>
|
||||
<string name="copy_link_toast">تم نسخ الرابط إلى الحافظة</string>
|
||||
<string name="delete">مسح</string>
|
||||
<string name="pause">وقف</string>
|
||||
<string name="update_notification_downloading">جارٍ تنزيل تحديث التطبيق…</string>
|
||||
<string name="subs_default_reset_toast">إعادة التعيين إلى القيمة العادية</string>
|
||||
<string name="season_short">س</string>
|
||||
<string name="episode_format" formatted="true">%d%s</string>
|
||||
<string name="no_chromecast_support_toast">لا يتمتع هذا المزود بدعم كرومكاست</string>
|
||||
<string name="no_links_found_toast">لم يتم العثور على أي روابط</string>
|
||||
<string name="play_episode_toast">تشغيل الحلقة</string>
|
||||
<string name="acra_report_toast">عذرًا، تعطل التطبيق. سيتم إرسال تقرير خطأ مجهول إلى المطورين</string>
|
||||
<string name="season_format">%s%d%s</string>
|
||||
<string name="no_season">لا يوجد موسم</string>
|
||||
<string name="episode">حلقة</string>
|
||||
<string name="episodes_range">%d-%d</string>
|
||||
<string name="episode_short">يي</string>
|
||||
<string name="clear_history">امسح التاريخ</string>
|
||||
<string name="update_notification_installing">جارٍ تثبيت تحديث التطبيق…</string>
|
||||
<string name="start">بدأ</string>
|
||||
<string name="no_episodes_found">لم يتم العثور على أي حلقات</string>
|
||||
<string name="enable_skip_op_from_database_des">إظهار تخطي النوافذ المنبثقة للفتح/الإنهاء</string>
|
||||
<string name="clipboard_too_large">الكثير من النص. غير قادر على الحفظ في الحافظة.</string>
|
||||
<string name="action_mark_as_watched">وضع علامة كما شاهدت</string>
|
||||
<string name="action_remove_from_watched">إزالة من شاهد</string>
|
||||
<string name="delete_file">حذف ملف</string>
|
||||
<string name="test_failed">فشل</string>
|
||||
<string name="resume">اكتمل</string>
|
||||
<string name="go_back_30">-30</string>
|
||||
<string name="go_forward_30">+30</string>
|
||||
<string name="history">تاريخ</string>
|
||||
<string name="confirm_exit_dialog">هل أنت متأكد أنك تريد الخروج؟</string>
|
||||
<string name="yes">نعم</string>
|
||||
<string name="no">لا</string>
|
||||
<string name="update_notification_failed">تعذر تثبيت الإصدار الجديد من التطبيق</string>
|
||||
<string name="apk_installer_legacy">إرث</string>
|
||||
<string name="apk_installer_package_installer">منزل المجموعة</string>
|
||||
<string name="sort_rating_asc">التقييم (من الأقل إلى الأعلى)</string>
|
||||
<string name="sort_updated_new">تم التحديث (من الجديد إلى القديم)</string>
|
||||
<string name="sort_updated_old">تم التحديث (القديم إلى الجديد)</string>
|
||||
<string name="sort_alphabetical_a">أبجديًا (من الألف إلى الياء)</string>
|
||||
<string name="empty_library_no_accounts_message">مكتبتك فارغة :(
|
||||
\nقم بتسجيل الدخول إلى حساب المكتبة أو قم بإضافة العروض إلى مكتبتك المحلية.</string>
|
||||
<string name="safe_mode_file">!تم العثور على ملف الوضع الآمن
|
||||
\n.عدم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف</string>
|
||||
<string name="revert">ارجع</string>
|
||||
<string name="subscription_in_progress_notification">تحديث العروض المشتركة</string>
|
||||
<string name="set_default">الوضع العادي</string>
|
||||
<string name="edit">حرر</string>
|
||||
<string name="profiles">ملفات تعريفية</string>
|
||||
<string name="help">مساعدة</string>
|
||||
<string name="quality_profile_help">.هنا يمكنك تغيير كيفية ترتيب المصادر. إذا كان للفيديو أولوية أعلى، فسيظهر في مكان أعلى في تحديد المصدر. مجموع أولوية المصدر وأولوية الجودة هو أولوية الفيديو
|
||||
\n
|
||||
\nالمصدر أ: 3
|
||||
\nالجودة ب: 7
|
||||
\nستكون أولوية الفيديو المدمجة .10
|
||||
\n
|
||||
\n!ملاحظة: إذا كان المجموع 10 أو أكثر، فسيقوم اللاعب تلقائيًا بتخطي التحميل عند تحميل هذا الرابط</string>
|
||||
<string name="already_voted">لقد صوت بالفعل</string>
|
||||
<string name="sort_alphabetical_z">أبجديًا (ياء إلى ألف)</string>
|
||||
<string name="sort_by">ترتيب حسب</string>
|
||||
<string name="subscription_list_name">مشترك</string>
|
||||
<string name="delayed_update_notice">سيتم تحديث التطبيق عند الخروج</string>
|
||||
<string name="sort">رتب</string>
|
||||
<string name="sort_rating_desc">التقييم (من الأعلى إلى الأقل)</string>
|
||||
<string name="select_library">حدد المكتبة</string>
|
||||
<string name="open_with">افتع مع</string>
|
||||
<string name="empty_library_logged_in_message">.هذه القائمة فارغة. حاول التبديل إلى واحد آخر</string>
|
||||
<string name="subscription_new">%sتم الاشتراك في</string>
|
||||
<string name="subscription_deleted">%sتم إلغاء الاشتراك من</string>
|
||||
<string name="subscription_episode_released">!%dتم إصدار الحلقة</string>
|
||||
<string name="profile_background_des">خلفية الملف الشخصي</string>
|
||||
<string name="profile_number">%dملف التعريف</string>
|
||||
<string name="wifi">واي فاي</string>
|
||||
<string name="mobile_data">بيانات الجوال</string>
|
||||
<string name="use">استخدم</string>
|
||||
<string name="unable_to_inflate">%sتعذر إنشاء واجهة المستخدم بشكل صحيح، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور</string>
|
||||
<string name="qualities">الصفات</string>
|
||||
</resources>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<string name="next_episode_time_min_format" formatted="true">%dm</string>
|
||||
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
|
||||
<string name="result_poster_img_des">Poster</string>
|
||||
<string name="search_poster_img_des">@string/result_poster_img_des</string>
|
||||
<string name="search_poster_img_des">Pôster</string>
|
||||
<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>
|
||||
|
@ -66,7 +66,7 @@
|
|||
<string name="error_loading_links_toast">Erro Carregando Links</string>
|
||||
<string name="download_storage_text">Armazenamento Interno</string>
|
||||
<string name="app_dubbed_text">Dub</string>
|
||||
<string name="app_subbed_text">Leg</string>
|
||||
<string name="app_subbed_text">Sub</string>
|
||||
<string name="popup_delete_file">Deletar Arquivo</string>
|
||||
<string name="popup_play_file">Assistir Arquivo</string>
|
||||
<string name="popup_resume_download">Retomar Download</string>
|
||||
|
@ -156,7 +156,7 @@
|
|||
<string name="bug_report_settings_on">Não enviar nenhum dado</string>
|
||||
<string name="show_fillers_settings">Mostrar episódios de Filler em anime</string>
|
||||
<string name="show_trailers_settings">Mostrar trailers</string>
|
||||
<string name="kitsu_settings">Mostrar posters do kitsu</string>
|
||||
<string name="kitsu_settings">Mostrar posters do Kitsu</string>
|
||||
<string name="pref_filter_search_quality">Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa</string>
|
||||
<string name="automatic_plugin_updates">Atualizações de plugin automáticas</string>
|
||||
<string name="updates_settings">Mostrar atualizações do app</string>
|
||||
|
@ -183,7 +183,7 @@
|
|||
<string name="season_short">S</string>
|
||||
<string name="episode_short">E</string>
|
||||
<string name="no_episodes_found">Nenhum Episódio encontrado</string>
|
||||
<string name="delete_file">Deletar Arquivo</string>
|
||||
<string name="delete_file">Apagar Arquivo</string>
|
||||
<string name="delete">Deletar</string>
|
||||
<string name="pause">Pausar</string>
|
||||
<string name="resume">Retomar</string>
|
||||
|
@ -257,7 +257,7 @@
|
|||
<string name="dont_show_again">Não mostrar de novo</string>
|
||||
<string name="skip_update">Pular essa Atualização</string>
|
||||
<string name="update">Atualizar</string>
|
||||
<string name="watch_quality_pref">Qualidade preferida</string>
|
||||
<string name="watch_quality_pref">Qualidade preferida de reprodução (Wi-fi)</string>
|
||||
<string name="limit_title">Máximo de caracteres do título de vídeos</string>
|
||||
<string name="limit_title_rez">Resolução do player de vídeo</string>
|
||||
<string name="video_buffer_size_settings">Tamanho do buffer do vídeo</string>
|
||||
|
@ -410,15 +410,19 @@
|
|||
<string name="batch_download_finish_format" formatted="true">Transferido %d %s com sucesso</string>
|
||||
<string name="batch_download_nothing_to_download_format" formatted="true">Tudo %s já transferido</string>
|
||||
<string name="batch_download">Transferência em batch</string>
|
||||
<string name="plugin_singular">plugin</string>
|
||||
<string name="plugin">plugins</string>
|
||||
<string name="plugin_singular">Plugin</string>
|
||||
<string name="plugin">Plugins</string>
|
||||
<string name="delete_repository_plugins">Isto irá apagar todos os repositórios de plugins</string>
|
||||
<string name="delete_repository">Apagar repositório</string>
|
||||
<string name="setup_extensions_subtext">Transferir lista de sites a usar</string>
|
||||
<string name="plugins_downloaded" formatted="true">Transferido: %d</string>
|
||||
<string name="plugins_disabled" formatted="true">Desativado: %d</string>
|
||||
<string name="plugins_not_downloaded" formatted="true">Não transferido: %d</string>
|
||||
<string name="blank_repo_message">Adicionar um repositório para instalar extensões de sites</string>
|
||||
<string name="blank_repo_message">CloudStream não tem fontes instaladas por padrão. Você precisa instalar um site de repositórios.
|
||||
\n
|
||||
\nPor causa das limitações do DMCA (Digital Millennium Copyright Act ) feito em nome de Sky UK Limited 🤮nós não podemos adicionar site de repositórios no app.
|
||||
\n
|
||||
\nEntre no nosso Discord ou pesquise online.</string>
|
||||
<string name="view_public_repositories_button">Ver repositórios da comunidade</string>
|
||||
<string name="view_public_repositories_button_short">Lista pública</string>
|
||||
<string name="uppercase_all_subtitles">Todas as legendas em maiúsculas</string>
|
||||
|
@ -428,4 +432,122 @@
|
|||
<string name="autoplay_next_settings_des">Começa o próximo episódio quando o atual termina</string>
|
||||
<string name="enable_nsfw_on_providers">Ativar NSFW em fornecedores compatíveis</string>
|
||||
<string name="category_providers">Fornecedores</string>
|
||||
<string name="revert">Reverter</string>
|
||||
<string name="pref_category_actions">Ações</string>
|
||||
<string name="already_voted">votou com sucesso</string>
|
||||
<string name="update_notification_downloading">Baixando atualização do aplicativo…</string>
|
||||
<string name="referer">Referencias</string>
|
||||
<string name="pref_category_app_updates">Atualizações do App</string>
|
||||
<string name="play_with_app_name">Tocar com CloudStream</string>
|
||||
<string name="automatic_plugin_download_summary">Automaticamente instale todos os plugins não instalados dos repositórios adicionados.</string>
|
||||
<string name="play_trailer_button">Reproduzir Trailer</string>
|
||||
<string name="browser">Navegador</string>
|
||||
<string name="pref_category_backup">Copia de Segurança</string>
|
||||
<string name="android_tv_interface_off_seek_settings_summary">A Barra de Progresso pode ser usada quando o player estiver oculto</string>
|
||||
<string name="subscription_list_name">Inscrever</string>
|
||||
<string name="empty_library_logged_in_message">Essa lista está vazia. Tente mudar para outra.</string>
|
||||
<string name="play_livestream_button">Reproduzir Livestream</string>
|
||||
<string name="test_log">Log do Teste</string>
|
||||
<string name="automatic_plugin_download">Baixa plugins automaticamente</string>
|
||||
<string name="automatic_plugin_download_mode_title">Selecione o modo para filtrar os plugins baixados</string>
|
||||
<string name="test_failed">Teste falhou</string>
|
||||
<string name="android_tv_interface_on_seek_settings_summary">A Barra de Progresso pode ser usada quando o player estiver visível</string>
|
||||
<string name="sort">Organizar</string>
|
||||
<string name="yes">Sim</string>
|
||||
<string name="confirm_exit_dialog">Você tem certeza que deseja sair\?</string>
|
||||
<string name="update_notification_installing">Instalando atualização do aplicativo…</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="profiles">Perfis</string>
|
||||
<string name="android_tv_interface_on_seek_settings">Exibindo Player - procure na Barra de Progresso</string>
|
||||
<string name="action_remove_from_watched">Remover dos assistidos</string>
|
||||
<string name="pref_category_extensions">Extensões</string>
|
||||
<string name="sort_alphabetical_a">Alfabética(A => Z)</string>
|
||||
<string name="open_with">Abrir com</string>
|
||||
<string name="select_library">Selecionar Biblioteca</string>
|
||||
<string name="test_passed">Passou</string>
|
||||
<string name="empty_library_no_accounts_message">Sua biblioteca está vazia :0
|
||||
\nEntre numa conta de biblioteca ou adicione Midias para sua biblioteca local.</string>
|
||||
<string name="watch_quality_pref_data">Qualidade preferida de reprodução (Dados Móveis)</string>
|
||||
<string name="apk_installer_legacy">Legado</string>
|
||||
<string name="library">Biblioteca</string>
|
||||
<string name="no">Não</string>
|
||||
<string name="tracks">Trilhas Sonoras</string>
|
||||
<string name="sort_rating_asc">Votação (Baixa para Alta)</string>
|
||||
<string name="update_started">Atualização iniciada</string>
|
||||
<string name="nsfw_singular">Conteúdo +18</string>
|
||||
<string name="help">Ajuda</string>
|
||||
<string name="redo_setup_process">Processo de configuração de Redo</string>
|
||||
<string name="update_notification_failed">Não pudemos instalar a nova versão do App</string>
|
||||
<string name="apk_installer_package_installer">instalador de pacotes</string>
|
||||
<string name="sort_by">Organizar por</string>
|
||||
<string name="sort_rating_desc">Votação (Alta para Baixa)</string>
|
||||
<string name="sort_alphabetical_z">Alfabética(Z => A)</string>
|
||||
<string name="qualities">Qualidade</string>
|
||||
<string name="profile_background_des">Perfil de plano de fundo</string>
|
||||
<string name="quality_profile_help">Aqui você pode alterar como as fontes são ordenadas. Se um vídeo tiver uma prioridade mais alta, aparecerá mais alto na seleção da fonte. A soma da prioridade da fonte e da prioridade da qualidade é a prioridade do vídeo.
|
||||
\n
|
||||
\nFonte A: 3
|
||||
\nQualidade B: 7
|
||||
\nTerá uma prioridade de vídeo combinada de 10.
|
||||
\n
|
||||
\nNOTA: Se a soma for 10 ou mais, o Player pulará automaticamente o carregamento quando o link for carregado!</string>
|
||||
<string name="safe_mode_file">Arquivo de modo de segurança encontrado!
|
||||
\nNão carregar nenhuma extensão na inicialização até que o arquivo seja removido.</string>
|
||||
<string name="subscription_new">Inscrevel em %d</string>
|
||||
<string name="subscription_episode_released">Episódio %d Lançado</string>
|
||||
<string name="set_default">Selecionar padrão</string>
|
||||
<string name="subscription_deleted">Disinscrevel em %d</string>
|
||||
<string name="apk_installer_settings_des">Alguns aparelhos não possuem suporte para este pacote de instalação. Tente a opção legada se a atualização não instalar.</string>
|
||||
<string name="mobile_data">Dados móveis</string>
|
||||
<string name="profile_number">Perfil %d</string>
|
||||
<string name="subscription_in_progress_notification">Atualizando shows inscritos</string>
|
||||
<string name="android_tv_interface_off_seek_settings">Player oculto - Procure na barra de progresso</string>
|
||||
<string name="nsfw">Conteúdo +18</string>
|
||||
<string name="restart">Reiniciar</string>
|
||||
<string name="stop">Parar</string>
|
||||
<string name="action_mark_as_watched">Marcar como assistido</string>
|
||||
<string name="delayed_update_notice">Aplicativo precisa ser fechado para atualizar</string>
|
||||
<string name="enable_skip_op_from_database_des">Mostrar popups pulados para abertura e finalização</string>
|
||||
<string name="episodes_range">%d-%d</string>
|
||||
<string name="player_settings_play_in_app">Player interno</string>
|
||||
<string name="extension_size">Tamanho</string>
|
||||
<string name="skip_type_op">Abrindo</string>
|
||||
<string name="season_format">%s %d%s</string>
|
||||
<string name="plugins_updated" formatted="true">%d plugins atualizados</string>
|
||||
<string name="safe_mode_description">Todos as extensões serão desligadas para ajuda se talvez estejam causando algum bug.</string>
|
||||
<string name="app_not_found_error">Aplicativo não encontrado</string>
|
||||
<string name="skip_type_recap">Recapitular</string>
|
||||
<string name="all_languages_preference">Todas as linguagens</string>
|
||||
<string name="skip_type_format" formatted="true">Pula %s</string>
|
||||
<string name="skip_type_mixed_ed">Mistura terminada</string>
|
||||
<string name="safe_mode_title">Modo seguro ligado</string>
|
||||
<string name="extension_rating" formatted="true">Ranquear: %s</string>
|
||||
<string name="extension_language">Linguagem</string>
|
||||
<string name="hls_playlist">Lista de reprodução HLS</string>
|
||||
<string name="skip_type_ed">Terminando</string>
|
||||
<string name="episode_format" formatted="true">%d %s</string>
|
||||
<string name="sort_updated_old">Adicionado em (antigo para novo)</string>
|
||||
<string name="skip_type_intro">Introdução</string>
|
||||
<string name="no_plugins_found_error">plug-ins não foram encontrados no repositório</string>
|
||||
<string name="no_repository_found_error">Repositório não encontrado, verifique o URL e tente usa uma VPN</string>
|
||||
<string name="extension_description">Descrição</string>
|
||||
<string name="extension_version">Versão</string>
|
||||
<string name="extension_authors">Autores</string>
|
||||
<string name="extension_install_first">Instale a extensão primeiro</string>
|
||||
<string name="skip_type_creddits">Créditos</string>
|
||||
<string name="history">Historico</string>
|
||||
<string name="clear_history">Limpar historico</string>
|
||||
<string name="clipboard_too_large">Tem Muito texto. Não é possível salvar no clipboard.</string>
|
||||
<string name="player_pref">Player de vídeo preferido</string>
|
||||
<string name="start">Começar</string>
|
||||
<string name="extension_types">Suportado</string>
|
||||
<string name="extension_status">Status</string>
|
||||
<string name="player_settings_play_in_mpv">MPV</string>
|
||||
<string name="skip_type_mixed_op">Abrindo mistura</string>
|
||||
<string name="player_settings_play_in_vlc">VLC</string>
|
||||
<string name="apply_on_restart">Aplicar quando reiniciar</string>
|
||||
<string name="safe_mode_crash_info">Visualização info de crash</string>
|
||||
<string name="audio_tracks">Faixas de áudio</string>
|
||||
<string name="sort_updated_new">Adicionado em (novo para antigo)</string>
|
||||
<string name="video_tracks">Faixas de video</string>
|
||||
</resources>
|
||||
|
|
|
@ -576,4 +576,5 @@
|
|||
<string name="no_plugins_found_error">V repozitáři nebyly nalezeny žádné doplňky</string>
|
||||
<string name="no_repository_found_error">Repozitář nenalezen, zkontrolujte adresu URL a zkuste použít VPN</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="already_voted">Již jste hlasovali</string>
|
||||
</resources>
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
<string name="sort_cancel">Abbrechen</string>
|
||||
<string name="sort_copy">Kopieren</string>
|
||||
<string name="sort_close">Schließen</string>
|
||||
<string name="sort_clear">Löschen</string>
|
||||
<string name="sort_clear">Leeren</string>
|
||||
<string name="sort_save">Speichern</string>
|
||||
<string name="player_speed">Player-Geschwindigkeit</string>
|
||||
<string name="subtitles_settings">Untertiteleinstellungen</string>
|
||||
|
@ -390,7 +390,7 @@
|
|||
<string name="skip_setup">Einrichtung überspringen</string>
|
||||
<string name="app_layout_subtext">Aussehen der App passend zu dem des Geräts ändern</string>
|
||||
<string name="crash_reporting_title">Absturzmeldung</string>
|
||||
<string name="preferred_media_subtext">Was möchtest du anschauen\?</string>
|
||||
<string name="preferred_media_subtext">Was möchten Sie sehen\?</string>
|
||||
<string name="setup_done">Fertig</string>
|
||||
<string name="extensions">Erweiterungen</string>
|
||||
<string name="add_repository">Repository hinzufügen</string>
|
||||
|
@ -546,4 +546,10 @@
|
|||
\nWerden eine kombinierte Videopriorität von 10 haben.
|
||||
\n
|
||||
\nHINWEIS: Wenn die Summe 10 oder mehr beträgt, überspringt der Player automatisch das Laden, wenn der Link geladen wird!</string>
|
||||
<string name="automatic_plugin_download_mode_title">Filtermodus für Plugin-Downloads auswählen</string>
|
||||
<string name="already_voted">Es wurde bereits abgestimmt</string>
|
||||
<string name="no_plugins_found_error">Keine Plugins im Repository gefunden</string>
|
||||
<string name="no_repository_found_error">Repository nicht gefunden, überprüfe die URL und probiere eine VPN</string>
|
||||
<string name="unable_to_inflate">Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein schwerwiegender Fehler und sollte sofort gemeldet werden. %s</string>
|
||||
<string name="disable">Deaktivieren</string>
|
||||
</resources>
|
||||
|
|
|
@ -552,4 +552,5 @@
|
|||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="no_plugins_found_error">No se encontraron complementos en el repositorio</string>
|
||||
<string name="no_repository_found_error">Repositorio no encontrado, comprueba la URL y prueba la VPN</string>
|
||||
<string name="already_voted">Ya has votado</string>
|
||||
</resources>
|
||||
|
|
2
app/src/main/res/values-fil/strings.xml
Normal file
2
app/src/main/res/values-fil/strings.xml
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources/>
|
|
@ -530,4 +530,26 @@
|
|||
<string name="android_tv_interface_on_seek_settings">Joueur représenté - Montant de la recherche</string>
|
||||
<string name="android_tv_interface_off_seek_settings">Joueur caché - Montant de la recherche</string>
|
||||
<string name="jsdelivr_enabled">Impossible d\'accéder à GitHub. Activation du proxy jsDelivr…</string>
|
||||
<string name="already_voted">Vous avez déjà voté</string>
|
||||
<string name="disable">Désactivé</string>
|
||||
<string name="quality_profile_help">Ici, vous pouvez modifier la façon dont les sources sont ordonnées. Si une vidéo a une priorité plus élevée, elle apparaîtra plus haut dans la sélection de la source. La somme de la priorité source et de la priorité qualité est la priorité vidéo.
|
||||
\n
|
||||
\nSource A : 3
|
||||
\nQualité B : 7
|
||||
\nLa priorité vidéo combinée sera de 10.
|
||||
\n
|
||||
\nREMARQUE : Si la somme est de 10 ou plus, le joueur sautera automatiquement le chargement lorsque ce lien est chargé !</string>
|
||||
<string name="no_plugins_found_error">Aucun plugin trouvé dans ce dossier</string>
|
||||
<string name="no_repository_found_error">Dossier non trouvé, vérifiez l\'url et essayé un VPN</string>
|
||||
<string name="mobile_data">Données mobiles</string>
|
||||
<string name="set_default">Définir par défaut</string>
|
||||
<string name="use">Utiliser</string>
|
||||
<string name="edit">Modifier</string>
|
||||
<string name="profiles">Profils</string>
|
||||
<string name="help">Aide</string>
|
||||
<string name="profile_number">Profil %d</string>
|
||||
<string name="wifi">Wi-Fi</string>
|
||||
<string name="qualities">Qualités</string>
|
||||
<string name="unable_to_inflate">L\'interface utilisateur n\'a pas pu être créée correctement. Il s\'agit d\'un bogue majeur qui doit être signalé immédiatement %s</string>
|
||||
<string name="automatic_plugin_download_mode_title">Sélectionnez le mode pour filtrer le téléchargement des plugins</string>
|
||||
</resources>
|
||||
|
|
|
@ -190,7 +190,7 @@
|
|||
<string name="backup_success">Adatok eltárolva</string>
|
||||
<string name="backup_failed_error_format">Hiba a biztonsági mentés során %s</string>
|
||||
<string name="category_account">Fiókok</string>
|
||||
<string name="advanced_search_des">Szolgáltatás szerinti keresés eredmények</string>
|
||||
<string name="advanced_search_des">Szolgáltató szerint elkülönítve adja meg a keresési eredményeket</string>
|
||||
<string name="bug_report_settings_on">Nem küld adatokat</string>
|
||||
<string name="kitsu_settings">Poszterek megjelenítése Kitsu-ról</string>
|
||||
<string name="pref_filter_search_quality">Kiválasztott videóminőségek elrejtése keresési eredményekbe</string>
|
||||
|
@ -198,7 +198,7 @@
|
|||
<string name="automatic_plugin_download">Bővítmények automatikus letöltése</string>
|
||||
<string name="automatic_plugin_download_summary">Automatikusan telepíti az összes még nem telepített bővítményt a hozzáadott tárolókból.</string>
|
||||
<string name="updates_settings">Alkalmazás frissítések megjelenítése</string>
|
||||
<string name="updates_settings_des">Automatikusan keressen új frissítéseket indításkor</string>
|
||||
<string name="updates_settings_des">Automatikusan keressen új frissítéseket indításkor.</string>
|
||||
<string name="uprereleases_settings">Frissítés az előzetes kiadásokhoz (prerelease)</string>
|
||||
<string name="uprereleases_settings_des">Csak előzetesen kiadott frissítések (prerelease) keresése a teljes kiadások helyett</string>
|
||||
<string name="github">Github</string>
|
||||
|
@ -232,30 +232,30 @@
|
|||
<string name="episode_action_play_in_browser">Lejátszás böngészőben</string>
|
||||
<string name="episode_action_download_subtitle">Feliratok letöltése</string>
|
||||
<string name="reload_error">Újracsatlakozás…</string>
|
||||
<string name="swipe_to_seek_settings_des">Swipe balra vagy jobbra a videólejátszóban az idő vezérléséhez</string>
|
||||
<string name="swipe_to_seek_settings_des">Húzd balra vagy jobbra a videólejátszóban az idő vezérléséhez</string>
|
||||
<string name="swipe_to_change_settings">Csúsztassa ujját a beállítások módosításához</string>
|
||||
<string name="swipe_to_change_settings_des">Csúsztassa az újját bal vagy jobb oldalon a fényerő vagy hangerő megváltoztatásához</string>
|
||||
<string name="swipe_to_change_settings_des">Csúsztassa felfelé vagy lefelé a bal vagy jobb oldalon a fényerő vagy a hangerő megváltoztatásához</string>
|
||||
<string name="backup_settings">Biztonsági mentés</string>
|
||||
<string name="benene_count_text_none">0 Banán a fejlesztőknek</string>
|
||||
<string name="swipe_to_seek_settings">Swipe to seek</string>
|
||||
<string name="swipe_to_seek_settings">Húzás a kereséshez</string>
|
||||
<string name="autoplay_next_settings">Következő epizód automatikus lejátszása</string>
|
||||
<string name="autoplay_next_settings_des">Következő epizód lejátszása amikor az aktuális epizód véget ér</string>
|
||||
<string name="double_tap_to_seek_settings">Dupla koppintás to seek</string>
|
||||
<string name="double_tap_to_seek_settings">Dupla koppintás a kereséshez</string>
|
||||
<string name="double_tap_to_pause_settings">Dupla koppintás a szüneteltetéshez</string>
|
||||
<string name="double_tap_to_seek_amount_settings">Player seek amount</string>
|
||||
<string name="double_tap_to_seek_amount_settings">Lejátszó keresési értéke (Másodpercben)</string>
|
||||
<string name="double_tap_to_seek_settings_des">Koppintson kétszer a jobb vagy bal oldalra az előre vagy hátra ugráshoz</string>
|
||||
<string name="double_tap_to_pause_settings_des">Koppintson középre a szüneteltetéshez</string>
|
||||
<string name="double_tap_to_pause_settings_des">Koppintson kétszer középen a szüneteltetéshez</string>
|
||||
<string name="use_system_brightness_settings">Rendszer fényerejének használata</string>
|
||||
<string name="use_system_brightness_settings_des">Rendszer fényerejének használata az appban a sötét átfedés helyett</string>
|
||||
<string name="episode_sync_settings">Előrehaladás frissítése</string>
|
||||
<string name="episode_sync_settings_des">Automatikusan szinkronizálja az aktuális epizód előrehaladását</string>
|
||||
<string name="restore_settings">Adatok visszaállítása a biztonsági mentésből</string>
|
||||
<string name="restore_settings">Adatok visszaállítása biztonsági mentésből</string>
|
||||
<string name="restore_success">Biztonsági mentés betöltve</string>
|
||||
<string name="settings_info">Információ</string>
|
||||
<string name="resume">Folytatás</string>
|
||||
<string name="go_back_30">-30</string>
|
||||
<string name="update_started">Frissítés elkezdődött</string>
|
||||
<string name="restore_failed_format" formatted="true">Nem sikerült visszaállítani az adatok a fájlból %s</string>
|
||||
<string name="restore_failed_format" formatted="true">Nem sikerült visszaállítani az adatokat a %s fájlból</string>
|
||||
<string name="backup_failed">Tárolási engedélyek hiányoznak. Kérjük próbálja újra.</string>
|
||||
<string name="bug_report_settings_off">Csak összeomlásokról küld adatokat</string>
|
||||
<string name="apk_installer_settings">APK Telepítő</string>
|
||||
|
@ -280,7 +280,7 @@
|
|||
<string name="dns_pref">DNS HTTPS-en keresztül</string>
|
||||
<string name="browser">Böngésző</string>
|
||||
<string name="pref_category_android_tv">Android TV</string>
|
||||
<string name="pref_category_gestures">kézmozdulatok</string>
|
||||
<string name="pref_category_gestures">Kézmozdulatok</string>
|
||||
<string name="skip_update">frissítés kihagyása</string>
|
||||
<string name="pref_category_app_updates">Alkalmazásfrissítések</string>
|
||||
<string name="category_providers">Szolgáltatók</string>
|
||||
|
@ -496,4 +496,18 @@
|
|||
<string name="quality_hq">HQ</string>
|
||||
<string name="plugins_downloaded" formatted="true">%d letöltve</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="emulator_layout">Emulátor elrendezés</string>
|
||||
<string name="add_sync">Nyomkövetés hozzáadása</string>
|
||||
<string name="phone_layout">Telefon elrendezés</string>
|
||||
<string name="bottom_title_settings">Poszter cím helye</string>
|
||||
<string name="bottom_title_settings_des">Tegye a címet a poszter alá</string>
|
||||
<string name="android_tv_interface_off_seek_settings_summary">Az átugrás mértéke, amikor a lejátszó el van rejtve</string>
|
||||
<string name="legal_notice">Jogi nyilatkozat</string>
|
||||
<string name="android_tv_interface_on_seek_settings">Lejátszó megjelenítve - Ugrási Érték</string>
|
||||
<string name="android_tv_interface_off_seek_settings">Lejátszó elrejtve - Ugrási Érték</string>
|
||||
<string name="add_site_pref">Klónozott oldal</string>
|
||||
<string name="add_site_summary">Egy meglévő webhely klónjának hozzáadása, más URL-címmel</string>
|
||||
<string name="tv_layout">TV elrendezés</string>
|
||||
<string name="automatic">Automatikus</string>
|
||||
<string name="android_tv_interface_on_seek_settings_summary">Az átugrás mértéke, amikor a lejátszó látható</string>
|
||||
</resources>
|
||||
|
|
|
@ -574,4 +574,6 @@
|
|||
<string name="automatic_plugin_download_mode_title">Pilih mode untuk memfilter unduhan plugin</string>
|
||||
<string name="no_plugins_found_error">Tidak ada plugin yang ditemukan di repositori</string>
|
||||
<string name="no_repository_found_error">Repositori tidak ditemukan, periksa URL dan coba VPN</string>
|
||||
<string name="already_voted">Kamu sudah voting</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
</resources>
|
||||
|
|
|
@ -574,4 +574,5 @@
|
|||
<string name="automatic_plugin_download_mode_title">Seleziona la modalità per filtrare il download dei plugin</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="disable">Disabilita</string>
|
||||
<string name="already_voted">Hai già votato</string>
|
||||
</resources>
|
||||
|
|
556
app/src/main/res/values-my/strings.xml
Normal file
556
app/src/main/res/values-my/strings.xml
Normal file
|
@ -0,0 +1,556 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="cast_format" formatted="true">သရုပ်ဆောင်များ: %s</string>
|
||||
<string name="next_episode_time_day_format" formatted="true">%dရက် %ddနာရီ %ddမိနစ်</string>
|
||||
<string name="next_episode_time_hour_format" formatted="true">%ddနာရီ %ddမိနစ်</string>
|
||||
<string name="next_episode_time_min_format" formatted="true">%ddမိနစ်</string>
|
||||
<string name="search_poster_img_des">ပိုစတာ</string>
|
||||
<string name="episode_poster_img_des">အပိုင်း ပိုစတာ</string>
|
||||
<string name="home_main_poster_img_des">မိန်း ပိုစတာ</string>
|
||||
<string name="home_next_random_img_des">နောက် ကျပန်း</string>
|
||||
<string name="go_back_img_des">နောက်သို့</string>
|
||||
<string name="preview_background_img_des">နောက်ခံပုံရိပ်ကို အကြိုကြည့်ရန်</string>
|
||||
<string name="rated_format" formatted="true">အဆင့်: %.1f</string>
|
||||
<string name="new_update_format" formatted="true">အပ်ဒိတ်အသစ်!
|
||||
\n%s -> %s</string>
|
||||
<string name="filler" formatted="true">စစ်ထုတ်မှု</string>
|
||||
<string name="duration_format" formatted="true">%d မိနစ်</string>
|
||||
<string name="app_name">CloudStream</string>
|
||||
<string name="play_with_app_name">CloudStream ဖြင့်ကြည့်ရန်</string>
|
||||
<string name="title_home">ပင်မ</string>
|
||||
<string name="title_search">ရှာရန်</string>
|
||||
<string name="search_hint">ရှာရန်…</string>
|
||||
<string name="search_hint_site" formatted="true">ရှာရန် %s…</string>
|
||||
<string name="no_data">အချက်အလက်မရှိပါ</string>
|
||||
<string name="episode_more_options_des">အခြားရွေးစရာများ</string>
|
||||
<string name="next_episode">နောက်အပိုင်း</string>
|
||||
<string name="result_tags">ကဏ္ဍများ</string>
|
||||
<string name="result_share">မျှဝေမည်</string>
|
||||
<string name="result_open_in_browser">ဘရောက်ဇာတွင်ဖွင့်ရန်</string>
|
||||
<string name="browser">ဘရောက်ဇာ</string>
|
||||
<string name="skip_loading">မစောင့်တော့ပါ</string>
|
||||
<string name="type_watching">ကြည့်နေသည်</string>
|
||||
<string name="type_completed">ကြည့်ပြီး</string>
|
||||
<string name="type_dropped">ကြည့်ခြင်းရပ်ထားသော</string>
|
||||
<string name="type_none">ဘာမျှ</string>
|
||||
<string name="error_loading_links_toast">လင့်များချိတ်ဆက်ရာတွင်အချို့အယွင်း</string>
|
||||
<string name="download_storage_text">ဖုန်း သိုလှောင်ရုံ</string>
|
||||
<string name="filter_bookmarks">စာမှတ်များ စစ်ထုတ်မှု</string>
|
||||
<string name="error_bookmarks_text">စာမှတ်များ</string>
|
||||
<string name="action_remove_from_bookmarks">ဖယ်ရှားရန်</string>
|
||||
<string name="action_add_to_bookmarks">ကြည့်ရှုမှုအခြေအနေသတ်မှတ်ခြင်း</string>
|
||||
<string name="sort_copy">မိတ္တူကူးရန်</string>
|
||||
<string name="sort_close">ပိတ်ရန်</string>
|
||||
<string name="sort_clear">ရှင်းလင်းရန်</string>
|
||||
<string name="sort_save">သိမ်းဆည်းရန်</string>
|
||||
<string name="player_speed">ကြည့်ရှုမှုအရှိန်</string>
|
||||
<string name="subs_background_color">နောက်ခံ အရောင်</string>
|
||||
<string name="subs_window_color">ဝင်းဒိုး အရောင်</string>
|
||||
<string name="subs_edge_type">အစွန်းနားပုံစံ</string>
|
||||
<string name="subs_subtitle_elevation">စာတန်းထိုး အမြင့်</string>
|
||||
<string name="subs_font">ဖောင့်</string>
|
||||
<string name="subs_font_size">ဖောင့် အရွယ်အစား</string>
|
||||
<string name="search_provider_text_types">အမျိုးအစားများအသုံးပြု၍ရှာရန်</string>
|
||||
<string name="benene_count_text">%d အက်ပ်ဖန်တီးသူတွေကိုကျေးဇူးတင်ကြောင်းပို့မည်</string>
|
||||
<string name="subs_auto_select_language">အလိုအလျောက် ဘာသာစကားရွေးချယ်ခြင်း</string>
|
||||
<string name="subs_download_languages">ဒေါင်းလုဒ် လုပ်ထားသော ဘာသာစကားများ</string>
|
||||
<string name="subs_hold_to_reset_to_default">မူရင်းပုံစံအတိုင်းပြန်ထားရန်ဖိထားပါ</string>
|
||||
<string name="continue_watching">ဆက်လက်ကြည့်ရှုမည်</string>
|
||||
<string name="action_remove_watching">ဖယ်ရှားရန်</string>
|
||||
<string name="action_open_watching">ပိုမို၍</string>
|
||||
<string name="action_open_play">@string/home_play</string>
|
||||
<string name="vpn_torrent">ဒီဟာကတောရပ်တစ်ခုပါ ဗီပီအန်တစ်ခုသုံးဖို့အကြံပြုပါတယ်</string>
|
||||
<string name="torrent_plot">ဖော်ပြချက်</string>
|
||||
<string name="normal_no_plot">ဇာတ်လမ်းသွား မတွေ့ပါ</string>
|
||||
<string name="torrent_no_plot">ဖော်ပြချက် မရိှပါ</string>
|
||||
<string name="show_log_cat">Logcat ပြရန် 🐈</string>
|
||||
<string name="test_log">Log</string>
|
||||
<string name="picture_in_picture">ရုပ်ပုံထပ်</string>
|
||||
<string name="player_size_settings">ကြည့်ရှုမှု စခရင်အရွယ်အစားချိန်ညိှမှု</string>
|
||||
<string name="player_subtitles_settings">စာတန်းထိုးများ</string>
|
||||
<string name="player_subtitles_settings_des">ကြည့်ရှုမှုစာတန်းထိုးပြုပြင်စရာများ</string>
|
||||
<string name="eigengraumode_settings">Eigengravy လုပ်ဆောင်မှု</string>
|
||||
<string name="swipe_to_seek_settings">ရစ်ရန်ဘယ်ညာဆွဲပါ</string>
|
||||
<string name="swipe_to_seek_settings_des">သင်ရောက်နေတဲ့နေရာပြောင်းရန်ဘယ်ညာဆွဲပါ</string>
|
||||
<string name="swipe_to_change_settings">ပြုပြင်စရာရိှပါက ပွတ်ဆွဲပါ</string>
|
||||
<string name="autoplay_next_settings">နောက်အပိုင်းကို အလိုအလျောက် ဖွင့်ပါ</string>
|
||||
<string name="double_tap_to_seek_settings">ကျော်ရန်နှစ်ချက်နှိပ်ပါ</string>
|
||||
<string name="double_tap_to_pause_settings">ရပ်ရန်နှစ်ချက်နှိပ်ပါ</string>
|
||||
<string name="double_tap_to_seek_amount_settings">ကျော်လိုသောပမာဏ (စက္ကန့်များ)</string>
|
||||
<string name="double_tap_to_seek_settings_des">ရှေ့သို့ကျော်ရန် သို့ နောက်သို့ရစ်ရန် ဘယ် သို့ ညာ ပေါ်မှာနှစ်ချက်နှိပ်ပါ</string>
|
||||
<string name="double_tap_to_pause_settings_des">ရပ်ရန် အလယ်တွင်နှစ်ချက်နှိပ်ပါ</string>
|
||||
<string name="use_system_brightness_settings">ဖုန်းအလင်းအမှောင်အတိုင်းသုံးမည်</string>
|
||||
<string name="use_system_brightness_settings_des">အက်ပ်ကြည့်ရှုမှုထဲမှာ ဖုန်းအလင်းအမှောင်အတိုင်းသုံးမည်</string>
|
||||
<string name="episode_sync_settings">ကြည့်ရှုမှုတိုးတက်ခြင်းကိုအပ်ဒိတ်လုပ်ပါ</string>
|
||||
<string name="restore_settings">အရန်သိမ်းဖိုင်မှပြန်သိုလှောင်မည်</string>
|
||||
<string name="backup_settings">အရန်သိမ်းမည်</string>
|
||||
<string name="restore_success">အရန်သိမ်းဖိုင်များရယူပြီး</string>
|
||||
<string name="restore_failed_format" formatted="true">အရန်သိမ်းဖိုင်မှပြန်သိုလှောငးခြင်မအောင်မြင်ပါ %s</string>
|
||||
<string name="backup_success">သိုလှောင်ပြီး</string>
|
||||
<string name="backup_failed">သိုလှောင်ရုံခွင့်ပြုချက်မရိှပါ။ပြန်ကြိုးစားပါ။</string>
|
||||
<string name="backup_failed_error_format">အရန်သိမ်းနေစဥ်အချို့အယွင်း %s</string>
|
||||
<string name="library">လိုက်ဘရီ</string>
|
||||
<string name="category_updates">အပ်ဒိတ်များနှင့်အရန်သိမ်းဆည်းမှု</string>
|
||||
<string name="advanced_search">နက်နက်ရှိုင်းရှိုင်းရှာခြင်း</string>
|
||||
<string name="advanced_search_des">သင့်ကိုဝန်ဆောင်မှုပေးသူအလိုက်ရှာဖွေမှုရလဒ်များပေးမည်</string>
|
||||
<string name="bug_report_settings_off">ချို့ယွင်းမှုအကြီးစားဖြစ်မှသာဒေတာများပေးပို့ပါ</string>
|
||||
<string name="show_fillers_settings">anime များအတွက်ဖြည့်စွက်အပိုင်းကိုပြရန်</string>
|
||||
<string name="show_trailers_settings">ထွေလာများကိုပြရန်</string>
|
||||
<string name="kitsu_settings">Kitsu မှ ပိုစတာများကိုပြရန်</string>
|
||||
<string name="automatic_plugin_updates">အလိုအလျောက် ဖြည့်စွက်လုပ်ဆောင်ချက်များကိုအပ်ဒိတ်တင်ခြင်း</string>
|
||||
<string name="automatic_plugin_download_mode_title">အပိုလုပ်ဆောင်ချက်များကိုစစ်ထုတ်ရန်မုဒ်ရွေးပါ</string>
|
||||
<string name="updates_settings">အက်ပ်အပ်ဒိတ်များပြရန်</string>
|
||||
<string name="redo_setup_process">အစီအစဥ်ချခြင်းကိုပြန်စမည်</string>
|
||||
<string name="apk_installer_settings">အက်ပ်ထည့်သွင်းခြင်း</string>
|
||||
<string name="apk_installer_settings_des">အချို့ဖုန်းတွေက အက်ပ်ထည့်သွင်းခြင်းလုပ်ဆောင်ချက်အသစ်ကို မပံ့ပိုးပါဘူး။အကယ်၍အလုပ်မဖြစ်ပါကသမားရိုးကျနည်းလမ်းကိုအသုံးပြုပါ။</string>
|
||||
<string name="github">Github</string>
|
||||
<string name="no_chromecast_support_toast">ဤဝန်ဆောင်မှုပေးသူသည် Chromecast ကိုမပံ့ပိုးပါ</string>
|
||||
<string name="no_links_found_toast">လင့်များမတွေ့ပါ</string>
|
||||
<string name="copy_link_toast">ကလစ်ဘုတ်သို့မိတ္တူကူးပြီး</string>
|
||||
<string name="play_episode_toast">အပိုင်းကြည့်မည်</string>
|
||||
<string name="season">အတွဲ</string>
|
||||
<string name="no_season">အတွဲမရှိပါ</string>
|
||||
<string name="episode">အပိုင်း</string>
|
||||
<string name="episodes">အပိုင်းများ</string>
|
||||
<string name="episodes_range">%d-%d</string>
|
||||
<string name="delete_message" formatted="true">ဒါကအပြီးဖျက်ခြင်းဖြစ်ပါသည် %s
|
||||
\nသင်သေချာပါသလား။</string>
|
||||
<string name="resume_time_left" formatted="true">%dမိနစ်
|
||||
\nကျန်ရိှသည်</string>
|
||||
<string name="status_ongoing">ထုတ်လွှင့်နေဆဲ</string>
|
||||
<string name="status_completed">ထုတ်လွှင့်မှုပြီးဆုံး</string>
|
||||
<string name="status">အခြေအနေ</string>
|
||||
<string name="year">ခုနစ်</string>
|
||||
<string name="rating">အဆင့်သတ်မှတ်ချက်</string>
|
||||
<string name="duration">ကြာချိန်</string>
|
||||
<string name="site">ဆိုဒ်</string>
|
||||
<string name="synopsis">အကျဥ်းချုပ်</string>
|
||||
<string name="queued">နောက်အစီအစဥ်</string>
|
||||
<string name="no_subtitles">စာတန်းထိုးမထည့်</string>
|
||||
<string name="default_subtitles">ပုံသေ</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="free_storage">ကျန်ရှိသော</string>
|
||||
<string name="app_storage">အက်ပ်</string>
|
||||
<string name="movies">ရုပ်ရှင်များ</string>
|
||||
<string name="tv_series">ဇာတ်လမ်းတွဲများ</string>
|
||||
<string name="cartoons">ကာတွန်းများ</string>
|
||||
<string name="anime">Anime</string>
|
||||
<string name="torrent">ေတာရပ်များ</string>
|
||||
<string name="documentaries">မှတ်တမ်းရုပ်ရှင်များ</string>
|
||||
<string name="ova">OVA</string>
|
||||
<string name="asian_drama">အာရှ ဒရာမာများ</string>
|
||||
<string name="livestreams">တိုက်ရိုက်ထုတ်လွှင်မှုများ</string>
|
||||
<string name="nsfw">အပြာဗီဒီယိုများ</string>
|
||||
<string name="others">အခြား</string>
|
||||
<string name="movies_singular">ရုပ်ရှင်</string>
|
||||
<string name="tv_series_singular">ဇာတ်လမ်းတွဲ</string>
|
||||
<string name="ova_singular">OVA</string>
|
||||
<string name="torrent_singular">တောရပ်</string>
|
||||
<string name="documentaries_singular">မှတ်တမ်းရုပ်ရှင်</string>
|
||||
<string name="asian_drama_singular">အာရှ ဒရာမာ</string>
|
||||
<string name="live_singular">တိုက်ရိုက်ထုတ်လွှင့်မှု</string>
|
||||
<string name="nsfw_singular">အပြာဗီဒီယို</string>
|
||||
<string name="other_singular">ဗီဒီယို</string>
|
||||
<string name="source_error">ရင်းမြစ်အချို့အယွင်း</string>
|
||||
<string name="remote_error">အဝေးထိန်းချုပ်မှုအချို့အယွင်း</string>
|
||||
<string name="render_error">တင်ဆက်သူ အချို့အယွင်း</string>
|
||||
<string name="unexpected_error">မျှော်လင့်မထားသော အချို့အယွင်း</string>
|
||||
<string name="episode_action_chromecast_episode">Chromecast အပိုင်း</string>
|
||||
<string name="episode_action_chromecast_mirror">Chromecast ဖန်သားပြင်</string>
|
||||
<string name="episode_action_play_in_app">အက်ပ်တွင်းဖွင့်</string>
|
||||
<string name="episode_action_play_in_format">ဖွင့်ရန် %s</string>
|
||||
<string name="episode_action_play_in_browser">ဘရောက်ဇာထဲမှာ ဖွင့်ရန်</string>
|
||||
<string name="episode_action_copy_link">လင့်ကူးယူရန်</string>
|
||||
<string name="episode_action_auto_download">အလိုအလျောက်ဒေါင်းလုဒ်</string>
|
||||
<string name="episode_action_reload_links">လင့်များကို ပြန်စစ်ရန်</string>
|
||||
<string name="show_hd">အရည်အသွေး အမှတ်အသား</string>
|
||||
<string name="show_dub">နောက်ခံအသံ အမှတ်အသား</string>
|
||||
<string name="show_sub">စာတန်း အမှတ်အသား</string>
|
||||
<string name="show_title">ခေါင်းစဥ်</string>
|
||||
<string name="no_update_found">အပ်ဒိတ်မရှိပါ</string>
|
||||
<string name="check_for_update">အပ်ဒိတ်စစ်ရန်</string>
|
||||
<string name="video_lock">လော့ခ်ခတ်ရန်</string>
|
||||
<string name="video_source">ရင်းမြစ်</string>
|
||||
<string name="video_skip_op">OPကိုကျော်ရန်</string>
|
||||
<string name="skip_update">ဒီအပ်ဒိတ်ကိုကျော်ပါ</string>
|
||||
<string name="update">အပ်ဒိတ်</string>
|
||||
<string name="limit_title">ခေါင်းစဥ်အတွက်စာလုံးရေပြည့်ခြင်း</string>
|
||||
<string name="limit_title_rez">ကြည့်ရှုမှု အရည်အသွေး</string>
|
||||
<string name="video_buffer_size_settings">ဗီဒီယိုရှေ့ပြေးသိမ်းဆည်းမှုပမာဏ</string>
|
||||
<string name="android_tv_interface_on_seek_settings_summary">ကြည့်ရှုမှုဘားမြင်တွေ့ရချိန်တွင်ပြသသောပမာဏ</string>
|
||||
<string name="android_tv_interface_off_seek_settings">ဝှက်ထားသောကြည့်ရှုပြီးသောပမာဏ</string>
|
||||
<string name="android_tv_interface_off_seek_settings_summary">ဝှက်ထားသည့်အခါ အသုံးပြုသည့် ရှာဖွေမှုပမာဏ</string>
|
||||
<string name="video_ram_description">Android TV ကဲ့သို့သော မမ်မိုရီနည်းသော စက်ပစ္စည်းများတွင် သတ်မှတ်နှုန်း အလွန်မြင့်မားပါက ပျက်စီးမှုများ ဖြစ်စေသည်။</string>
|
||||
<string name="dns_pref">DNS over HTTPS</string>
|
||||
<string name="jsdelivr_proxy">raw.githubusercontent.com ပရောက်စီ</string>
|
||||
<string name="jsdelivr_enabled">GitHub သို့ မရောက်ရှိနိုင်ပါ။ jsDelivr ပရောက်စီကို ဖွင့်နေသည်…</string>
|
||||
<string name="add_site_pref">ကလုန်း ဆိုဒ်</string>
|
||||
<string name="remove_site_pref">ဆိုဒ်ကိုဖယ်ရှားရန်</string>
|
||||
<string name="add_site_summary">မတူညီသော URL တစ်ခုဖြင့် ရှိပြီးသား ဝဘ်ဆိုက်တစ်ခု၏ ပုံတူတစ်ခုကို ထည့်ပါ</string>
|
||||
<string name="download_path_pref">ဒေါင်းလုဒ်လမ်းကြောင်း</string>
|
||||
<string name="nginx_url_pref">NGINX ဆာဗာ URL</string>
|
||||
<string name="display_subbed_dubbed_settings">Dubbed/Subbed Anime ကိုပြသပါ</string>
|
||||
<string name="resize_fit">မျက်နှာပြင်နှင့် အံကိုက်</string>
|
||||
<string name="resize_fill">ဆန့်သည်</string>
|
||||
<string name="resize_zoom">ချဲ့သည်</string>
|
||||
<string name="legal_notice">ရှင်းလင်းချက်</string>
|
||||
<string name="pref_category_bypass">ISP ရှောင်လွှဲမှုများ</string>
|
||||
<string name="pref_category_links">လင့်များ</string>
|
||||
<string name="pref_category_app_updates">အက်ပ်အပ်ဒိတ်များ</string>
|
||||
<string name="pref_category_extensions">Extensions</string>
|
||||
<string name="pref_category_actions">ဆောင်ရွက်ချက်များ</string>
|
||||
<string name="pref_category_cache">Cache</string>
|
||||
<string name="pref_category_android_tv">Android တီဗွီ</string>
|
||||
<string name="pref_category_subtitles">စာတန်းထိုးများ</string>
|
||||
<string name="pref_category_defaults">ပုံသေများ</string>
|
||||
<string name="pref_category_looks">ပုံပန်းသဏ္ဌာန်</string>
|
||||
<string name="category_general">အထွေထွေ</string>
|
||||
<string name="random_button_settings_desc">ပင်မစာမျက်နှာမှာကျပန်းခလုတ်ကိုပြပါ</string>
|
||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s အပိုင်း %d</string>
|
||||
<string name="next_episode_format" formatted="true">အပိုင်း %d ထုတ်လွှင့်ပြသမည်</string>
|
||||
<string name="result_poster_img_des">ပိုစတာ</string>
|
||||
<string name="home_change_provider_img_des">ပံ့ပိုးပေးသောဝန်ဆောင်မှုပြောင်းရန်</string>
|
||||
<string name="player_speed_text_format" formatted="true">အရှိန် (%.2fx)</string>
|
||||
<string name="title_downloads">ဒေါင်းလုဒ်များ</string>
|
||||
<string name="title_settings">ပြင်ဆင်ရန်</string>
|
||||
<string name="loading">ခဏစောင့်ပါ…</string>
|
||||
<string name="type_on_hold">ကြည့်ဆဲ</string>
|
||||
<string name="type_plan_to_watch">ကြည့်ရန်</string>
|
||||
<string name="type_re_watching">ပြန်ကြည့်နေသည်</string>
|
||||
<string name="pick_subtitle">စာတန်းထိုး</string>
|
||||
<string name="play_movie_button">ရုပ်ရှင်ကြည့်မည်</string>
|
||||
<string name="play_trailer_button">ထွေလာ ကြည့်မည်</string>
|
||||
<string name="play_livestream_button">လိုက်ခ် ကြည့်မည်</string>
|
||||
<string name="play_torrent_button">တောရပ် ကြည့်မည်</string>
|
||||
<string name="pick_source">ရင်းမြစ်များ</string>
|
||||
<string name="reload_error">ချိတ်ဆက်မှုပြန်ကြိုးစား…</string>
|
||||
<string name="go_back">နောက်သို့</string>
|
||||
<string name="play_episode">အပိုင်း ကြည့်မည်</string>
|
||||
<string name="download">ဒေါင်းလုဒ်</string>
|
||||
<string name="downloaded">ဒေါင်းလုဒ် လုပ်ပြီး</string>
|
||||
<string name="downloading">ဒေါင်းလုဒ် လုပ်နေသည်</string>
|
||||
<string name="download_paused">ဒေါင်းလုဒ် ရပ်ထား</string>
|
||||
<string name="download_started">ဒေါင်းလုဒ်စတင်</string>
|
||||
<string name="download_failed">ဒေါင်းလုဒ် မအောင်မြင်</string>
|
||||
<string name="download_canceled">ဒေါင်းလုဒ် ပယ်ဖျက်ပြီး</string>
|
||||
<string name="download_done">ဒေါင်းလုဒ်ပြီးစီး</string>
|
||||
<string name="update_started">အပ်ဒိတ်စတင်</string>
|
||||
<string name="stream">တိုက်ရိုက်ကြည့်မည်</string>
|
||||
<string name="app_dubbed_text">နောက်ခံအသံ</string>
|
||||
<string name="pref_filter_search_quality">ရှာဖွေမှုရလဒ်များတွင်ရွေးချယ်ထားသောဗီဒီယိုအရည်အသွေးကိုဝှက်ထားရန်</string>
|
||||
<string name="app_subbed_text">စာတန်းထိုး</string>
|
||||
<string name="popup_delete_file">ဖိုင်ဖျက်ရန်</string>
|
||||
<string name="popup_play_file">ဖိုင်ကို ဖွင့်ရန်</string>
|
||||
<string name="popup_resume_download">ဒေါင်းလုဒ် ဆက်လုပ်ရန်</string>
|
||||
<string name="popup_pause_download">ဒေါင်းလုဒ် ရပ်ရန်</string>
|
||||
<string name="pref_disable_acra">အလိုအလျောက်အက်ပ်ချို့ယွင်းချက်ပေးပို့ခြင်းကိုပိတ်မည်</string>
|
||||
<string name="home_more_info">ပိုမို၍</string>
|
||||
<string name="search_provider_text_providers">ပ့ံပိုးပေးသောဝန်ဆောင်မှုများအသုံးပြု၍ရှာရန်</string>
|
||||
<string name="home_expanded_hide">ဝုက်ရန်</string>
|
||||
<string name="benene_count_text_none">ကျေးဇူးတင်ကြောင်းမပို့ရသေး</string>
|
||||
<string name="home_play">ကြည့်မည်</string>
|
||||
<string name="home_info">အချက်အလက်</string>
|
||||
<string name="subs_subtitle_languages">စာတန်းထိုး ဘာသာစကား</string>
|
||||
<string name="subs_import_text" formatted="true">ဒီမှာနေရာချခြင်းဖြင့်ဖောင့်များကိုသွင်းပါ %s</string>
|
||||
<string name="sort_apply">အတည်ပြု</string>
|
||||
<string name="sort_cancel">ပယ်ဖျက်ရန်</string>
|
||||
<string name="subtitles_settings">စာတန်းထိုး ပြုပြင်ခြင်း</string>
|
||||
<string name="subs_text_color">စာသား အရောင်</string>
|
||||
<string name="subs_outline_color">အနားကွပ် အရောင်</string>
|
||||
<string name="vpn_might_be_needed">ဒီပံ့ပိုးမှုကောင်းမွန်စွာအလုပ်လုပ်ရန်ဗီပီအန်တစ်ခုလိုနိုင်ပါတယ်</string>
|
||||
<string name="provider_info_meta">အသေးစိတ်အချက်အလက်များပြမထားပါ။ဝဘ်ဆိုဒ်ပေါ်မှာမရှိလျှင်ကြည့်ရှု၍မရနိုင်ပါ။</string>
|
||||
<string name="automatic_plugin_download">ဖြည့်စွက်လုပ်ဆောင်ချက်များကို အလိုအလျောက်ဒေါင်းလုဒ်လုပ်ခြင်း</string>
|
||||
<string name="automatic_plugin_download_summary">ရီပိုစစ်ထရီများမှမထည့်သွင်းရသေးသောဖြည့်စွက်လုပ်ဆောင်ချက်များအားလုံးကိုထည့်သွင်းပါ။</string>
|
||||
<string name="picture_in_picture_des">ပြန်ကြည့်ခြင်းကိုအသေးစား ကြည့်ရှုမှုတွင်ဆက်ပြပါ</string>
|
||||
<string name="player_size_settings_des">အနက်ရောင်ဘောင်များကို ဖယ်ရှားရန်</string>
|
||||
<string name="updates_settings_des">အက်ပ်ထဲဝင်လိုက်သည့်နှင့်အက်ပ်အပ်ဒိတ်ကိုစစ်ဆေးပါ။</string>
|
||||
<string name="chromecast_subtitles_settings">Chromecast စာတန်းထိုးများ</string>
|
||||
<string name="chromecast_subtitles_settings_des">Chromecast စာတန်းထိုး ပြုပြင်ရန်</string>
|
||||
<string name="eigengraumode_settings_des">ကြည့်ရှုမှုပုံစံထဲမှာအရိှန်ရွေးစရာတစ်ခုထည့်ရန်</string>
|
||||
<string name="swipe_to_change_settings_des">အသံအတိုးအကျယ်နှင့်အလင်းအမှောင်များကိုချိန်ညိှရန် ဘယ် သို့ ညာ ဘက်တွင် အပေါ်အောက်ဆွဲပါ</string>
|
||||
<string name="autoplay_next_settings_des">ယခုကြည့်နေသောအပိုင်းပြီးပါကနောက်အပိုင်းကိုဖွင့်ပါ</string>
|
||||
<string name="episode_sync_settings_des">သင့်၏အပိုင်းကြည်ရှုမှုရောက်ရှိနေရာကိုအလိုအလျောက်သိမ်းဆည်းပါ</string>
|
||||
<string name="search">ရှာရန်</string>
|
||||
<string name="category_account">အကောင့်များ</string>
|
||||
<string name="settings_info">အချက်အလက်</string>
|
||||
<string name="bug_report_settings_on">ဒေတာများမပို့ရန်</string>
|
||||
<string name="android_tv_interface_on_seek_settings">ကြည့်ရှုပြီးသောအချိန်ပမာဏ</string>
|
||||
<string name="video_disk_description">Android TV ကဲ့သို့သော သိုလှောင်မှုနေရာနည်းပါးသော စက်ပစ္စည်းများတွင် အလွန်မြင့်မားစွာ သတ်မှတ်ပါက ပြဿနာများ ဖြစ်လာနိုင်သည်။</string>
|
||||
<string name="dns_pref_summary">ISP ပိတ်ဆို့ခြင်းကို ကျော်လွှားရန်အတွက် အသုံးဝင်သည်</string>
|
||||
<string name="jsdelivr_proxy_summary">jsDelivr ကို အသုံးပြု၍ GitHub ပိတ်ဆို့ခြင်းကို ကျော်ဖြတ်သည်။ အပ်ဒိတ်များကို ရက်အနည်းငယ်ကြာအောင် နှောင့်နှေးစေနိုင်သည်။</string>
|
||||
<string name="pref_category_backup">အရန်သိမ်းထားသော</string>
|
||||
<string name="pref_category_gestures">လက်ဟန်များ</string>
|
||||
<string name="pref_category_player_features">ကြည့်ရှုမှုလုပ်ဆောင်ချက်များ</string>
|
||||
<string name="pref_category_player_layout">အပြင်အဆင်</string>
|
||||
<string name="pref_category_ui_features">လုပ်ဆောင်ချက်များ</string>
|
||||
<string name="random_button_settings">ကျပန်းခလုတ်</string>
|
||||
<string name="uprereleases_settings">ရှေ့ပြေးအပ်ဒိတ်များကိုထည့်သွင်းပါ</string>
|
||||
<string name="uprereleases_settings_des">ပုံမှန်အပ်ဒိတ်များအစား ရှေ့ပြေးအက်ဒိတ်များကိုရှာပါ</string>
|
||||
<string name="lightnovel">တူညီသောအက်ပ်ရေးသားသူများ၏ ဝတ္ထုရှည်များဖတ်နိုင်သည့် အက်ပ်</string>
|
||||
<string name="anim">တူညီသောအက်ပ်ရေးသားသူများ၏ Anime အက်ပ်</string>
|
||||
<string name="discord">Discord ကိုဝင်ရန်</string>
|
||||
<string name="benene">အက်ပ်ရေးသားသူများထံ ကျေးဇူးတင်စာပို့မည်</string>
|
||||
<string name="benene_des">ပေးခဲ့သောစာအရေအတွက်</string>
|
||||
<string name="app_language">အက်ပ်ဘာသာစကား</string>
|
||||
<string name="subs_default_reset_toast">မူလအခြေအနေများကိုပြန်ထားပါ</string>
|
||||
<string name="acra_report_toast">စိတ်မကောင်းပါ။အက်ပ်ရပ်တန့်သွားပါတယ်။အမည်မဖော်ထားတဲ့တင်ပြချက်ကို အက်ပ်ရေးသားသူများထံ ပို့မှာဖြစ်ပါတယ်</string>
|
||||
<string name="season_format">%s %d%s</string>
|
||||
<string name="season_short">အတွဲ</string>
|
||||
<string name="episode_format" formatted="true">%d %s</string>
|
||||
<string name="episode_short">အပိုင်း</string>
|
||||
<string name="no_episodes_found">အပိုင်းများမတွေ့ပါ</string>
|
||||
<string name="delete_file">ဖိုင်ကိုဖျက်ရန်</string>
|
||||
<string name="delete">ဖျက်ရန်</string>
|
||||
<string name="pause">ရပ်ရန်</string>
|
||||
<string name="start">စရန်</string>
|
||||
<string name="test_failed">မအောင်မြင်ပါ</string>
|
||||
<string name="test_passed">ကျော်ဖြတ်ပြီး</string>
|
||||
<string name="resume">ကြည့်လက်စ</string>
|
||||
<string name="go_back_30">-30</string>
|
||||
<string name="go_forward_30">+30</string>
|
||||
<string name="used_storage">အသုံးပြုပြီးသော</string>
|
||||
<string name="cartoons_singular">ကာတွန်း</string>
|
||||
<string name="anime_singular">Anime</string>
|
||||
<string name="storage_error">ဒေါင်းလုဒ် အချို့အယွင်း၊သိုလှောင်ရုံခွင့်ပြုချက်တွေကိုစစ်ဆေးပါ</string>
|
||||
<string name="episode_action_download_mirror">ဒေါင်းလုဒ် ကြေးမုံ</string>
|
||||
<string name="episode_action_download_subtitle">စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ရန်</string>
|
||||
<string name="poster_ui_settings">ပိုစတာပေါ်ရှိ UI အစိတ်အပိုင်းများကို ပြောင်းပါ</string>
|
||||
<string name="video_aspect_ratio_resize">ပြန်ညိှ</string>
|
||||
<string name="dont_show_again">နောက်ထပ်မပြရန်</string>
|
||||
<string name="watch_quality_pref">ဝိုင်ဖိုင်ဖြင့်ကြည့်စဥ်ဗီဒီယိုအရည်အသွေး</string>
|
||||
<string name="watch_quality_pref_data">မိုဘိုင်းဒေတာဖြင့်ကြည့်စဥ်ဗီဒီယိုအရည်အသွေး</string>
|
||||
<string name="video_buffer_length_settings">ဗီဒီယိုရှေ့ပြေးသိမ်းဆည်းမှုအကွာအဝေး</string>
|
||||
<string name="video_buffer_disk_settings">ဗီဒီယိုcacheအများ</string>
|
||||
<string name="video_buffer_clear_settings">ဗီဒီယို cache နှင့် ရုပ်ပုံ cache များကိူရှင်းလင်းရန်</string>
|
||||
<string name="enable_nsfw_on_providers">ပံ့ပိုးပေးထားသည့် ဝန်ဆောင်မှုများပေါ်တွင် အပြာဗီဒီယို ကို ဖွင့်ပါ</string>
|
||||
<string name="provider_lang_settings">ဝန်ဆောင်မှုပံ့ပိုးသူဘာသာစကား</string>
|
||||
<string name="app_layout">အက်ပ်အပြင်အဆင်</string>
|
||||
<string name="preferred_media_settings">ဦးစားပေးမီဒီယာ</string>
|
||||
<string name="app_theme_settings">အက်ပ် အပြင်အဆင်</string>
|
||||
<string name="bottom_title_settings">ပိုစတာခေါင်းစဉ်တည်နေရာ</string>
|
||||
<string name="bottom_title_settings_des">ခေါင်းစဉ်ကို ပိုစတာအောက်မှာ ထားပါ</string>
|
||||
<string name="phone_layout">ဖုန်းအပြင်အဆင်</string>
|
||||
<string name="emulator_layout">အင်မြူလိတ်တာ အပြင်အဆင်</string>
|
||||
<string name="primary_color_settings">အဓိကအရောင်</string>
|
||||
<string name="recommended">အကြံပြုသည်</string>
|
||||
<string name="quality_dvd">ဒီဗွီဒီ</string>
|
||||
<string name="quality_4k">4K</string>
|
||||
<string name="network_adress_example">ထုတ်လွှင့်ရန် လင့်ခ် နှင့်ချိတ်ပါ</string>
|
||||
<string name="referer">ရည်ညွှန်းသည်</string>
|
||||
<string name="next">ရှေ့သို့</string>
|
||||
<string name="previous">နောက်သို့</string>
|
||||
<string name="plugins_updated" formatted="true">အပ်ဒိတ်လုပ်ပြီး %d ဖြည့်စွက်များ</string>
|
||||
<string name="plugins_not_downloaded" formatted="true">ဒေါင်းလုဒ်မလုပ်ရသေး: %d</string>
|
||||
<string name="player_settings_play_in_browser">ဝဘ်ဘရောက်ဇာ</string>
|
||||
<string name="app_not_found_error">အက်ပ်မတွေ့ပါ</string>
|
||||
<string name="all_languages_preference">ဘာသာစကားအားလုံး</string>
|
||||
<string name="skip_type_format" formatted="true">ကျော်ရန် %s</string>
|
||||
<string name="skip_type_recap">အစမှပြန်စ</string>
|
||||
<string name="skip_type_mixed_ed">ရောထားသောအဆုံးပိုင်း</string>
|
||||
<string name="skip_type_mixed_op">ရောထားသောအစပိုင်း</string>
|
||||
<string name="skip_type_creddits">ခရက်ဒစ်များ</string>
|
||||
<string name="skip_type_intro">အစ</string>
|
||||
<string name="yes">သေချာသည်</string>
|
||||
<string name="apk_installer_legacy">သမားရိုးကျ</string>
|
||||
<string name="apk_installer_package_installer">ထည့်သွင်းသူ</string>
|
||||
<string name="delayed_update_notice">ထွက်ချိန်တွင် အက်ပ်ကို အပ်ဒိတ်လုပ်ပါမည်</string>
|
||||
<string name="blank_repo_message">CloudStream တွင် မူရင်းအတိုင်း ထည့်သွင်းထားသည့်ဆိုက်များ မရှိပါ။ ရီပိုစစ်ထရီများ မှ ဆိုဒ် များကို ထည့်သွင်းရန်လိုအပ်သည်။
|
||||
\n
|
||||
\nSky UK Limited မှ ဦးနှောက်မဲ့ DMCA ကို ဖယ်ရှားလိုက်ခြင်းကြောင့် 🤮 ကျွန်ုပ်တို့သည် ရီပိုစစ်ထရီဆိုဒ်ကို အက်ပ်တွင် ချိတ်ဆက်၍မရပါ။
|
||||
\n
|
||||
\nကျွန်ုပ်တို့၏ Discord တွင်ပါဝင်ပါ သို့မဟုတ် အွန်လိုင်းတွင်ရှာဖွေပါ။</string>
|
||||
<string name="view_public_repositories_button">အခြားသူများ၏ရီပိုစစ်ထရီများကိုရှာဖွေမည်</string>
|
||||
<string name="tracks">အသံများ</string>
|
||||
<string name="audio_tracks">အသံဖိုင်များ</string>
|
||||
<string name="video_tracks">ဗီဒီယိုအသံဖိုင်များ</string>
|
||||
<string name="apply_on_restart">ပြန်စတင်ချိန်မှာအသုံးပြုပါ</string>
|
||||
<string name="stop">ရပ်ရန်</string>
|
||||
<string name="safe_mode_title">လုံခြုံသောမုဖွင့်ရန်</string>
|
||||
<string name="player_settings_play_in_app">ဖုန်းတွင်းကြည့်ရှုမှု</string>
|
||||
<string name="player_pref">ဦးစားပေး ဗီဒီယိုဖွင့်စက်</string>
|
||||
<string name="safe_mode_description">ပြဿနာဖြစ်စေသည့်အရာကို သင်ရှာဖွေရာတွင် အထောက်အကူဖြစ်စေရန်အတွက် ပျက်စီးမှုတစ်ခုကြောင့် အဆက်များအားလုံးကို ပိတ်ထားသည်။</string>
|
||||
<string name="extension_rating" formatted="true">အဆင့်သတ်မှတ်ချက်များ: %s</string>
|
||||
<string name="safe_mode_crash_info">ပျက်စီးမှုအချက်အလက်ကို ကြည့်ပါ</string>
|
||||
<string name="extension_description">ဖော်ပြချက်</string>
|
||||
<string name="extension_version">ဗားရှင်း</string>
|
||||
<string name="extension_status">အခြေအနေ</string>
|
||||
<string name="extension_size">အရွယ်အစား</string>
|
||||
<string name="extension_authors">ရေးသားသူများ</string>
|
||||
<string name="hls_playlist">HLS ဖွင့်စဥ်</string>
|
||||
<string name="history">ကြည့်ရှုခဲ့သည်များ</string>
|
||||
<string name="update_notification_failed">အက်ပ်၏ ဗားရှင်းအသစ်ကို ထည့်သွင်း၍မရပါ</string>
|
||||
<string name="enable_skip_op_from_database_des">အစပိုင်း/အဆုံးပိုင်းအတွက် ကျော်နိုင်သော ပေါ့ပ်အပ်များကို ပြပါ</string>
|
||||
<string name="clipboard_too_large">စာသားအလွန်များသဖြင့်ကလစ်ဘုတ်တွင် သိမ်းဆည်း၍မရပါ။</string>
|
||||
<string name="category_providers">ပံ့ပိုးပေးသူများ</string>
|
||||
<string name="category_ui">အပြင်အဆင်</string>
|
||||
<string name="automatic">အလိုအလျောက်</string>
|
||||
<string name="tv_layout">တီဗွီအပြင်အဆင်</string>
|
||||
<string name="example_password">သင့်စကားဝှက်</string>
|
||||
<string name="example_username">သင့်ယူဇာနိမ်း</string>
|
||||
<string name="example_email">သင့်အီးမေးလ် လိပ်စာ</string>
|
||||
<string name="example_ip">127.0.0.1</string>
|
||||
<string name="example_site_name">သင့်ဆိုဒ်</string>
|
||||
<string name="example_site_url">example.com</string>
|
||||
<string name="subtitles_shadow">အရိပ်</string>
|
||||
<string name="subtitles_raised">ထမြောက်မှု</string>
|
||||
<string name="subtitle_offset">စာတန်းထိုးများ ထပ်တူပြုရန်</string>
|
||||
<string name="subtitle_offset_extra_hint_later_format">စာတန်းထိုးများ အလွန်စောနေပါက %d ms ဒီဟာကိုသုံးပါ</string>
|
||||
<string name="subtitles_example_text">The quick brown fox jumps over the lazy dog</string>
|
||||
<string name="player_loaded_subtitles" formatted="true">တင်ပြီး %s</string>
|
||||
<string name="player_load_subtitles">ဖိုင်မှတင်သွင်းပြီး</string>
|
||||
<string name="quality_cam_hd">ရုံရိုက်</string>
|
||||
<string name="quality_hq">အကြည်</string>
|
||||
<string name="quality_hd">အကြည်</string>
|
||||
<string name="quality_uhd">UHD</string>
|
||||
<string name="quality_hdr">HDR</string>
|
||||
<string name="quality_sdr">SDR</string>
|
||||
<string name="quality_webrip">Web</string>
|
||||
<string name="quality_workprint">WP</string>
|
||||
<string name="quality_sd">SD</string>
|
||||
<string name="category_player">ကြည့်ရှုမှု</string>
|
||||
<string name="poster_image">ပိုစတာပုံရိပ်</string>
|
||||
<string name="subtitles_remove_bloat">စာတန်းထိုးများမှ bloat ကိုဖယ်ရှားပါ</string>
|
||||
<string name="subtitles_filter_lang">နှစ်သက်ရာ မီဒီယာဘာသာစကားဖြင့် စစ်ထုတ်ပါ</string>
|
||||
<string name="extras">အပိုများ</string>
|
||||
<string name="error_invalid_data">ဒေတာမမှန်ပါ</string>
|
||||
<string name="error_invalid_url">URL မမှန်ပါ</string>
|
||||
<string name="error">အချို့အယွင်း</string>
|
||||
<string name="subtitles_remove_captions">စာတန်းထိုးများမှ ပိတ်ထားသော စာတန်းများကို ဖယ်ရှားပါ</string>
|
||||
<string name="trailer">ထွေလာ</string>
|
||||
<string name="plugin">ဖြည့်စွက်များ</string>
|
||||
<string name="delete_repository_plugins">ရီပိုစစ်ထရီ ဖြည့်စွက်များအားလုံးကိုဖျက်မည်ဖြစ်သည်</string>
|
||||
<string name="delete_repository">ရီပိုစစ်ထရီ ကိုဖျက်ရန်</string>
|
||||
<string name="download_all_plugins_from_repo">ဤရီပိုစစ်ထရီမှ ဖြည့်စွက်များအားလုံးကို ဒေါင်းလုဒ်လုပ်မှာလား\?</string>
|
||||
<string name="single_plugin_disabled" formatted="true">%s (ပိတ်ပြီး)</string>
|
||||
<string name="extension_types">ထောက်ပံ့ထားသော</string>
|
||||
<string name="extension_language">ဘာသာစကား</string>
|
||||
<string name="extension_install_first">အဆက်များကိုအရင်သွင်းပါ</string>
|
||||
<string name="player_settings_play_in_vlc">VLC</string>
|
||||
<string name="player_settings_play_in_mpv">MPV</string>
|
||||
<string name="player_settings_play_in_web">ဝဘ်ထဲတွင်ဖွင့်ရန်</string>
|
||||
<string name="skip_type_op">အစပိုင်း</string>
|
||||
<string name="skip_type_ed">အဆုံးပိုင်း</string>
|
||||
<string name="clear_history">ကြည့်ရှုခဲ့သည်များကိုရှင်းရန်</string>
|
||||
<string name="action_remove_from_watched">ကြည့်ပြီးသည်မှဖယ်ရှားရန်</string>
|
||||
<string name="confirm_exit_dialog">သင်ထွက်ရန်သေချာပြီလား</string>
|
||||
<string name="no">မသေချာပါ</string>
|
||||
<string name="update_notification_downloading">အက်ပ်အပ်ဒိတ်အားဒေါင်းလုဒ်လုပ်နေသည်…</string>
|
||||
<string name="update_notification_installing">အက်ပ်အပ်ဒိတ်အားသွင်းနေသည်…</string>
|
||||
<string name="sort_updated_new">အပ်ဒိတ်ဖြစ်မှု (အသစ် မှ အဟောင်း)</string>
|
||||
<string name="empty_library_no_accounts_message">သင့်လိုက်ဘရီသည် ဗလာဖြစ်နေသည် :(
|
||||
\nအကောင့်ဝင်ပါ သို့မဟုတ် သင့်ဖုန်းလိုက်ဘရီတွင် ကြည့်စရာများထည့်ပါ။</string>
|
||||
<string name="use">သုံးရန်</string>
|
||||
<string name="edit">တည်းဖြတ်ရန်</string>
|
||||
<string name="qualities">အရည်အသွေးများ</string>
|
||||
<string name="subtitles_encoding">စာတန်းထိုး ကုဒ်လုပ်ခြင်း</string>
|
||||
<string name="category_provider_test">ပံ့ပိုးပေးသူ စစ်ဆေးမှု</string>
|
||||
<string name="login_format" formatted="true">%s %s</string>
|
||||
<string name="login">အကောင့်ဝင်မည်</string>
|
||||
<string name="switch_account">အကောင့်ပြောင်းမည်</string>
|
||||
<string name="upload_sync">ချိန်ညိှခြင်း</string>
|
||||
<string name="sync_total_episodes_some" formatted="true">/%d</string>
|
||||
<string name="player_load_subtitles_online">အင်တာနက်မှ တင်သွင်းမည်</string>
|
||||
<string name="actor_background">နောက်ခံ</string>
|
||||
<string name="authenticated_user_fail" formatted="true">အကောင့်မဝင်ရောက်နိုင်ပါ %s</string>
|
||||
<string name="none">ဘာမျှ</string>
|
||||
<string name="min">အနည်းဆုံး</string>
|
||||
<string name="subtitles_outline">အနားကွပ်</string>
|
||||
<string name="home_random">ကျပန်း</string>
|
||||
<string name="subtitles_depressed">ချုံ့ပြီး</string>
|
||||
<string name="subtitle_offset_hint">1000 ms</string>
|
||||
<string name="subtitle_offset_title">စာတန်းထိုး ကြန့်ကြာမှု</string>
|
||||
<string name="subtitle_offset_extra_hint_before_format">စာတန်းထိုးများအလွန်နောက်ကျနေပါက %d ms ဒီဟာကိုသုံးပါ</string>
|
||||
<string name="subtitle_offset_extra_hint_none_format">စာတန်းထိုး ကြန့်ကြာမှု သတ်မှတ်ထားခြင်းမရှိ</string>
|
||||
<string name="quality_cam">ရုံရိုက်</string>
|
||||
<string name="quality_cam_rip">ရုံရိုက်</string>
|
||||
<string name="quality_tc">TC</string>
|
||||
<string name="resolution">အရည်အသွေး</string>
|
||||
<string name="skip_setup">အစီအစဥ်ချခြင်းကိုကျော်မည်</string>
|
||||
<string name="crash_reporting_title">ချို့ယွင်းမှုသတင်းပေးပို့ခြင်း</string>
|
||||
<string name="preferred_media_subtext">ဘာတွေကြည့်ချင်လဲ</string>
|
||||
<string name="setup_done">ပြီးပြီ</string>
|
||||
<string name="extensions">အဆက်များ</string>
|
||||
<string name="plugin_load_fail" formatted="true">မသွင်းနိုင်ပါ %s</string>
|
||||
<string name="app_layout_subtext">သင့်စက်ပစ္စည်းနှင့် ကိုက်ညီစေရန် အက်ပ်၏အသွင်အပြင်ကို ပြောင်းလဲပါ</string>
|
||||
<string name="add_repository">ရီပိုစစ်ထရီထည့်ရန်</string>
|
||||
<string name="is_adult">အသက်ပြည့်ပြီးသူများသာ</string>
|
||||
<string name="repository_name_hint">ရီပိုစစ်ထရီအမည်</string>
|
||||
<string name="repository_url_hint">ရီပိုစစ်ထရီ URL</string>
|
||||
<string name="plugin_loaded">ဖြည့်စွက်များ ထည့်ပြီး</string>
|
||||
<string name="provider_languages_tip">ဤဘာသာစကားများဖြင့် ဗီဒီယိုများကို ကြည့်ရှုပါ</string>
|
||||
<string name="plugin_downloaded">ဖြည့်စွက်များ ဒေါင်းလုဒ်လုပ်ပြီး</string>
|
||||
<string name="plugin_deleted">ဖြည့်စွက်များဖျက်ပြီး</string>
|
||||
<string name="batch_download_start_format" formatted="true">ဒေါင်းလုဒ်လုပ်ခြင်း စတင်သည် %d %s…</string>
|
||||
<string name="batch_download_finish_format" formatted="true">ဒေါင်းလုဒ်လုပ်ပြီး %d %s</string>
|
||||
<string name="batch_download_nothing_to_download_format" formatted="true">အားလုံး %s ဒေါင်းလုဒ်လုပ်ပြီးသား</string>
|
||||
<string name="no_plugins_found_error">ရီပိုစစ်ထရီထဲတွင်ဖြည့်စွက်များမတွေ့ပါ</string>
|
||||
<string name="no_repository_found_error">ရီပိုစစ်ထရီမတွေ့ပါ၊URLကိုပြန်စစ်ပြီးဗီပီအန်ဖြင့်ကြိုးစားကြည့်ပါ</string>
|
||||
<string name="batch_download">အသုတ်လိုက် ဒေါင်းလုဒ်</string>
|
||||
<string name="plugin_singular">ဖြည့်စွက်</string>
|
||||
<string name="setup_extensions_subtext">သင်အသုံးပြုလိုသောဆိုက်များစာရင်းကို ဒေါင်းလုဒ်လုပ်ပါ</string>
|
||||
<string name="plugins_downloaded" formatted="true">ဒေါင်းလုဒ်လုပ်ပြီး: %d</string>
|
||||
<string name="plugins_disabled" formatted="true">ပိတ်ပြီး: %d</string>
|
||||
<string name="example_lang_name">ဘာသာစကားကုဒ် (en)</string>
|
||||
<string name="account">အကောင့်</string>
|
||||
<string name="logout">အကောင့်ထွက်မည်</string>
|
||||
<string name="add_account">အကောင့်ထည့်မည်</string>
|
||||
<string name="create_account">အကောင့်ဖွင့်မည်</string>
|
||||
<string name="add_sync">စောင့်ကြည့်ခြင်းထည့်မည်</string>
|
||||
<string name="added_sync_format" formatted="true">ထည့်ပြီး %s</string>
|
||||
<string name="sync_score">အဆင့်သတ်မှတ်ထားပြီး</string>
|
||||
<string name="sync_score_format" formatted="true">%d / 10</string>
|
||||
<string name="sync_total_episodes_none">/\?\?</string>
|
||||
<string name="authenticated_user" formatted="true">%s ချိတ်ဆက်ပြီး</string>
|
||||
<string name="disable">ပိတ်ပါ</string>
|
||||
<string name="normal">ပုံမှန်</string>
|
||||
<string name="all">အားလုံး</string>
|
||||
<string name="max">အပြည့်</string>
|
||||
<string name="downloaded_file">ဖိုင်ဒေါင်းလုဒ်လုပ်ပြီး</string>
|
||||
<string name="actor_main">အဓိက</string>
|
||||
<string name="actor_supporting">ထောက်ပံ့သည်</string>
|
||||
<string name="home_source">ရင်းမြစ်</string>
|
||||
<string name="coming_soon">မကြာမီလာမည်…</string>
|
||||
<string name="quality_ts">TS</string>
|
||||
<string name="quality_blueray">Blu-ray</string>
|
||||
<string name="resolution_and_title">အရည်အသွေးနှင့်ခေါင်းစဥ်</string>
|
||||
<string name="title">ခေါင်းစဥ်</string>
|
||||
<string name="error_invalid_id">အိုင်ဒီမမှန်ပါ</string>
|
||||
<string name="view_public_repositories_button_short">အများမြင်နိုင်သော</string>
|
||||
<string name="uppercase_all_subtitles">စာတန်းထိုးအားလုံးကို စာလုံးအကြီးပြောင်းပါ</string>
|
||||
<string name="restart">ပြန်စတင်မည်</string>
|
||||
<string name="action_mark_as_watched">ကြည့်ပြီးအဖြစ်မှတ်ရန်</string>
|
||||
<string name="sort_by">အစီအစဥ်ချမှု</string>
|
||||
<string name="sort">အစီအစဥ်</string>
|
||||
<string name="sort_rating_desc">အဆင့်သတ်မှတ်ချက် (အမြင့်ဆုံးမှအနိမ့်ဆုံးသို့)</string>
|
||||
<string name="sort_rating_asc">အဆင့်သတ်မှတ်ချက် (အနိမ့်ဆုံး မှ အမြင့်ဆုံးသို့)</string>
|
||||
<string name="sort_updated_old">အပ်ဒိတ်ဖြစ်မှု (အဟောင် မှ အသစ်)</string>
|
||||
<string name="sort_alphabetical_a">အက္ခရာစဥ်လိုက် (A မှ Z)</string>
|
||||
<string name="sort_alphabetical_z">အက္ခရာစဥ်လိုက် (Z မှ A)</string>
|
||||
<string name="revert">ပြောင်းပြန်</string>
|
||||
<string name="subscription_in_progress_notification">စာရင်းသွင်းထားသောရှိုးများကိုအပ်ဒိတ်လုပ်နေသည်</string>
|
||||
<string name="subscription_list_name">စာရင်းသွင်းပြီး</string>
|
||||
<string name="subscription_new">စာရင်းသွင်းပြီး %s</string>
|
||||
<string name="subscription_deleted">စာရင်းသွင်းမှုပယ်ဖျက်ပြီး %s</string>
|
||||
<string name="empty_library_logged_in_message">ဤစာရင်းသည် ဗလာဖြစ်နေသည်။ အခြားတစ်ခုသို့ ပြောင်းကြည့်ပါ။</string>
|
||||
<string name="safe_mode_file">Safe mode ဖိုင်ကို တွေ့ရှိခဲ့သည်။
|
||||
\nဖိုင်ကိုမဖယ်ရှားမချင်း စတင်ဖွင့်စတွင် မည်သည့် extension များကိုမျှ မတင်ပါ။</string>
|
||||
<string name="subscription_episode_released">အပိုင်းသစ် %d ထွက်ပြီ</string>
|
||||
<string name="profile_number">ပရိုဖိုင် %d</string>
|
||||
<string name="wifi">ဝိုင်ဖိုင်</string>
|
||||
<string name="mobile_data">မိုဘိုင်းဒေတာ</string>
|
||||
<string name="set_default">ပုံသေထားရန်</string>
|
||||
<string name="profiles">ပရိုဖိုင်များ</string>
|
||||
<string name="help">အကူအညီ</string>
|
||||
<string name="quality_profile_help">ဤနေရာတွင် သင်သည် အရင်းအမြစ်များကို မည်ကဲ့သို့ အစီအစဥ်ချမည်ကို ပြောင်းလဲနိုင်သည်။ ဗီဒီယိုတစ်ခုတွင် ပိုမိုဦးစားပေးပါက ရင်းမြစ်ရွေးချယ်မှုတွင် ပိုမိုမြင့်မားလာမည်ဖြစ်သည်။ အရင်းအမြစ် ဦးစားပေးနှင့် အရည်အသွေး ဦးစားပေး၏ ပေါင်းစုသည် ဗီဒီယို ဦးစားပေးဖြစ်သည်။
|
||||
\n
|
||||
\nအရင်းအမြစ် A: 3
|
||||
\nအရည်အသွေး B: 7
|
||||
\nပေါင်းစပ်ဗီဒီယို ဦးစားပေး 10 ခု ရှိပါမည်။
|
||||
\n
|
||||
\nမှတ်ချက်- ပေါင်းလဒ်သည် 10 သို့မဟုတ် ထို့ထက်ပိုပါက ထိုလင့်ခ်ကို တင်သည့်အခါ ဗီဒီယိုဖွင့်စက်သည် အလိုအလျောက် ဒေါင်းလုဒ်ကို ကျော်သွားမည်ဖြစ်သည်</string>
|
||||
<string name="profile_background_des">ပရိုဖိုင်နောက်ခံ</string>
|
||||
<string name="unable_to_inflate">UI ကို မှန်ကန်စွာ ဖန်တီး၍မရပါ၊ ၎င်းသည် အဓိက ချို့ယွင်းချက်တစ်ခုဖြစ်ပြီး ချက်ချင်းသတင်းပို့သင့်သည်။ %s</string>
|
||||
<string name="already_voted">သင်နဂိုတည်းကသတ်မှတ်ပြီး</string>
|
||||
<string name="select_library">လိုက်ဘရီရွေးချယ်ရန်</string>
|
||||
<string name="open_with">ဖြင့်ဖွင့်မည်</string>
|
||||
</resources>
|
|
@ -574,4 +574,5 @@
|
|||
<string name="disable">Uitzetten</string>
|
||||
<string name="unable_to_inflate">De gebruikersinterface kon niet correct worden gemaakt, dit is een ERNSTIG PROBLEEM en moet onmiddellijk gerapporteerd worden %s</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="already_voted">Je hebt al gestemd</string>
|
||||
</resources>
|
||||
|
|
|
@ -553,4 +553,7 @@
|
|||
<string name="automatic_plugin_download_mode_title">Wybierz tryb filtrowania pobieranych rozszerzeń</string>
|
||||
<string name="disable">Wyłączać</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="no_plugins_found_error">Nie znaleziono żadnych wtyczek w repozytorium</string>
|
||||
<string name="already_voted">Już oddano głos</string>
|
||||
<string name="no_repository_found_error">Nie znaleziono tego repozytorium, sprawdź adres URL lub spróbuj połączyć się przez VPN</string>
|
||||
</resources>
|
||||
|
|
|
@ -552,4 +552,5 @@
|
|||
<string name="disable">Desativar</string>
|
||||
<string name="no_plugins_found_error">Não foram encontrados plugins no repositório</string>
|
||||
<string name="no_repository_found_error">Repositório não encontrado, verifique o URL e tente a VPN</string>
|
||||
<string name="already_voted">Você já votou</string>
|
||||
</resources>
|
||||
|
|
|
@ -248,4 +248,5 @@
|
|||
<string name="clear_history">aoaaaaaoooghhh</string>
|
||||
<string name="sort_save">oooooh uuaagh</string>
|
||||
<string name="action_open_play">@string/home_play</string>
|
||||
<string name="chromecast_subtitles_settings">oouuhhh ahhooo-ahah</string>
|
||||
</resources>
|
||||
|
|
6
app/src/main/res/values-ti/strings.xml
Normal file
6
app/src/main/res/values-ti/strings.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_dub_sub_episode_text_format" formatted="true">%s ክፋል %d</string>
|
||||
<string name="next_episode_format" formatted="true">ክፋል %d በ ላይ ይወጣል</string>
|
||||
<string name="cast_format" formatted="true">ተዋሳእቲ፡ %s</string>
|
||||
</resources>
|
|
@ -80,14 +80,14 @@
|
|||
<string name="normal_no_plot">Сюжет не знайдено</string>
|
||||
<string name="torrent_no_plot">Опис не знайдено</string>
|
||||
<string name="show_log_cat">Показати Logcat 🐈</string>
|
||||
<string name="picture_in_picture_des">Продовження відтворення в мініатюрному плеєрі поверх інших застосунків</string>
|
||||
<string name="picture_in_picture_des">Продовжує відтворення в мініатюрному плеєрі поверх інших застосунків</string>
|
||||
<string name="player_size_settings_des">Прибирає чорні рамки</string>
|
||||
<string name="player_subtitles_settings">Субтитри</string>
|
||||
<string name="chromecast_subtitles_settings">Субтитри Chromecast</string>
|
||||
<string name="chromecast_subtitles_settings_des">Налаштування субтитрів Chromecast</string>
|
||||
<string name="eigengraumode_settings">Режим Eigengravy</string>
|
||||
<string name="swipe_to_change_settings">Проведіть пальцем, щоб змінити налаштування</string>
|
||||
<string name="swipe_to_change_settings_des">Проведіть пальцем вгору або вниз, ліворуч або праворуч, щоб змінити яскравість чи гучність</string>
|
||||
<string name="swipe_to_change_settings_des">Проведіть вгору або вниз з лівого або правого боку, щоб змінити яскравість чи гучність</string>
|
||||
<string name="autoplay_next_settings_des">Відтворювати наступний епізод після закінчення поточного</string>
|
||||
<string name="title_home">Головна</string>
|
||||
<string name="app_name">CloudStream</string>
|
||||
|
@ -121,8 +121,8 @@
|
|||
<string name="subs_text_color">Колір тексту</string>
|
||||
<string name="subs_outline_color">Колір контуру</string>
|
||||
<string name="autoplay_next_settings">Автовідтворення наступного епізоду</string>
|
||||
<string name="swipe_to_seek_settings_des">Проведіть пальцем з боку в бік, щоб керувати своїм положенням у відео</string>
|
||||
<string name="benene_count_text">%d Бананів для розробників</string>
|
||||
<string name="swipe_to_seek_settings_des">Проведіть з боку в бік, щоб керувати своїм положенням у відео</string>
|
||||
<string name="benene_count_text">%d бананів для розробників</string>
|
||||
<string name="player_size_settings">Кнопка зміни розміру плеєра</string>
|
||||
<string name="action_open_play">@string/home_play</string>
|
||||
<string name="vpn_might_be_needed">Для коректної роботи цього постачальника може знадобитися VPN</string>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<string name="swipe_to_seek_settings">Проведіть пальцем, щоб перемотати</string>
|
||||
<string name="double_tap_to_seek_settings">Двічі торкніться, щоб перемотати</string>
|
||||
<string name="double_tap_to_pause_settings">Двічі торкніться для паузи</string>
|
||||
<string name="double_tap_to_seek_amount_settings">Крок перемотки (Секунди)</string>
|
||||
<string name="double_tap_to_seek_amount_settings">Крок перемотки (секунди)</string>
|
||||
<string name="double_tap_to_pause_settings_des">Натисніть двічі посередині, щоб призупинити відтворення відео</string>
|
||||
<string name="use_system_brightness_settings">Використовувати яскравість системи</string>
|
||||
<string name="episode_sync_settings">Оновити прогрес перегляду</string>
|
||||
|
@ -150,8 +150,8 @@
|
|||
<string name="advanced_search_des">Надає результати пошуку, розділені за постачальниками</string>
|
||||
<string name="bug_report_settings_off">Надсилає дані лише про збої</string>
|
||||
<string name="bug_report_settings_on">Не надсилає даних</string>
|
||||
<string name="show_fillers_settings">Показати заповнюючий епізод для аніме</string>
|
||||
<string name="show_trailers_settings">Показати трейлери</string>
|
||||
<string name="show_fillers_settings">Показувати філери до аніме</string>
|
||||
<string name="show_trailers_settings">Показувати трейлери</string>
|
||||
<string name="pref_filter_search_quality">Приховати вибрану якість відео в результатах пошуку</string>
|
||||
<string name="automatic_plugin_download">Автоматичне завантаження плагінів</string>
|
||||
<string name="updates_settings">Показувати оновлення застосунку</string>
|
||||
|
@ -214,12 +214,12 @@
|
|||
<string name="episode_action_download_mirror">Завантажити дзеркало</string>
|
||||
<string name="check_for_update">Перевірити наявність оновлень</string>
|
||||
<string name="video_lock">Заблокувати</string>
|
||||
<string name="video_skip_op">Пропустити OP</string>
|
||||
<string name="video_skip_op">Пропускати OP</string>
|
||||
<string name="dont_show_again">Не показувати знову</string>
|
||||
<string name="update">Оновити</string>
|
||||
<string name="watch_quality_pref">Бажана якість перегляду (WiFi)</string>
|
||||
<string name="show_title">Заголовок</string>
|
||||
<string name="poster_ui_settings">Перемикання елементів інтерфейсу на плакаті</string>
|
||||
<string name="poster_ui_settings">Перемикання елементів інтерфейсу на постері</string>
|
||||
<string name="no_update_found">Оновлення не знайдено</string>
|
||||
<string name="double_tap_to_seek_settings_des">Двічі торкніться праворуч або ліворуч, щоб перемотати відео вперед або назад</string>
|
||||
<string name="use_system_brightness_settings_des">Використовуйте системну яскравість у плеєрі замість темної накладки</string>
|
||||
|
@ -227,10 +227,10 @@
|
|||
<string name="torrent">Торренти</string>
|
||||
<string name="episode_sync_settings_des">Автоматична синхронізація прогресу поточного епізоду</string>
|
||||
<string name="backup_failed">Відсутні дозволи на зберігання. Будь ласка, спробуйте ще раз.</string>
|
||||
<string name="kitsu_settings">Показати постери від Kitsu</string>
|
||||
<string name="kitsu_settings">Показувати постери від Kitsu</string>
|
||||
<string name="automatic_plugin_updates">Автоматичне оновлення плагінів</string>
|
||||
<string name="automatic_plugin_download_summary">Автоматично встановлювати всі ще не встановлені плагіни з доданих репозиторіїв.</string>
|
||||
<string name="updates_settings_des">Автоматично шукати нові оновлення після запуску застосунку.</string>
|
||||
<string name="updates_settings_des">Автоматично шукає нові оновлення після запуску застосунку.</string>
|
||||
<string name="uprereleases_settings">Оновлення до бета-версій</string>
|
||||
<string name="copy_link_toast">Посилання скопійовано в буфер обміну</string>
|
||||
<string name="apk_installer_settings_des">Деякі телефони не підтримують новий інсталятор пакетів. Спробуйте стару версію, якщо оновлення не встановлюються.</string>
|
||||
|
@ -354,7 +354,7 @@
|
|||
<string name="dns_pref">DNS через HTTPS</string>
|
||||
<string name="download_path_pref">Шлях завантаження</string>
|
||||
<string name="add_site_summary">Додайте клон існуючого сайту, з іншою URL-адресою</string>
|
||||
<string name="display_subbed_dubbed_settings">Відображати мітку Дубляж/Субтитри в аніме</string>
|
||||
<string name="display_subbed_dubbed_settings">Відображати мітку Дубляж/Субтитри до аніме</string>
|
||||
<string name="legal_notice">Застереження</string>
|
||||
<string name="pref_category_extensions">Розширення</string>
|
||||
<string name="pref_category_actions">Дії</string>
|
||||
|
@ -382,7 +382,7 @@
|
|||
<string name="actor_supporting">Підтримка</string>
|
||||
<string name="actor_background">Фон</string>
|
||||
<string name="quality_blueray">Blu-ray</string>
|
||||
<string name="subtitles_remove_captions">Видалити закриті титри з субтитрів</string>
|
||||
<string name="subtitles_remove_captions">Видаляти закриті титри з субтитрів</string>
|
||||
<string name="quality_dvd">DVD</string>
|
||||
<string name="error_invalid_data">Недійсні дані</string>
|
||||
<string name="subtitles_filter_lang">Фільтрувати за бажаною мовою медіа</string>
|
||||
|
@ -400,7 +400,7 @@
|
|||
<string name="quality_hd">HD</string>
|
||||
<string name="quality_ts">TS</string>
|
||||
<string name="quality_tc">TC</string>
|
||||
<string name="subtitles_remove_bloat">Видалити роздуття субтитрів</string>
|
||||
<string name="subtitles_remove_bloat">Видаляти роздуття субтитрів</string>
|
||||
<string name="referer">Referer</string>
|
||||
<string name="next">Далі</string>
|
||||
<string name="provider_languages_tip">Дивіться відео на цих мовах</string>
|
||||
|
@ -418,7 +418,7 @@
|
|||
<string name="batch_download_start_format" formatted="true">Почалося завантаження %d %s…</string>
|
||||
<string name="batch_download_finish_format" formatted="true">Завантажено %d %s</string>
|
||||
<string name="batch_download_nothing_to_download_format" formatted="true">Всі %s вже завантажено</string>
|
||||
<string name="batch_download">Пакетне завантаження</string>
|
||||
<string name="batch_download">Завантажити пакети</string>
|
||||
<string name="plugin_singular">плагін</string>
|
||||
<string name="plugin">плагіни</string>
|
||||
<string name="delete_repository">Видалити репозиторій</string>
|
||||
|
@ -451,8 +451,8 @@
|
|||
<string name="player_settings_play_in_app">Вбудований плеєр</string>
|
||||
<string name="player_settings_play_in_vlc">VLC</string>
|
||||
<string name="player_settings_play_in_mpv">MPV</string>
|
||||
<string name="player_settings_play_in_web">Відтворення веб-відео</string>
|
||||
<string name="player_settings_play_in_browser">Веб-браузер</string>
|
||||
<string name="player_settings_play_in_web">Web Video Cast</string>
|
||||
<string name="player_settings_play_in_browser">Веббраузер</string>
|
||||
<string name="skip_type_ed">Ендінґ</string>
|
||||
<string name="skip_type_recap">Коротке повторення</string>
|
||||
<string name="skip_type_format" formatted="true">Пропустити %s</string>
|
||||
|
@ -462,7 +462,7 @@
|
|||
<string name="skip_type_intro">Вступ</string>
|
||||
<string name="clear_history">Очистити історію</string>
|
||||
<string name="history">Історія</string>
|
||||
<string name="enable_skip_op_from_database_des">Показувати спливаючі вікна для опенінґу/ендінґу</string>
|
||||
<string name="enable_skip_op_from_database_des">Показує спливаюче вікно для пропуску опенінґу/ендінґу</string>
|
||||
<string name="clipboard_too_large">Забагато тексту. Не вдалося зберегти в буфер обміну.</string>
|
||||
<string name="action_mark_as_watched">Позначити як переглянуте</string>
|
||||
<string name="confirm_exit_dialog">Ви впевнені що хочете вийти\?</string>
|
||||
|
@ -552,4 +552,5 @@
|
|||
<string name="default_account">@string/default_subtitles</string>
|
||||
<string name="no_repository_found_error">Репозиторій не знайдено, перевірте URL-адресу та спробуйте VPN</string>
|
||||
<string name="no_plugins_found_error">Не знайдено жодних плагінів у репозиторії</string>
|
||||
<string name="already_voted">Ви вже проголосували</string>
|
||||
</resources>
|
||||
|
|
|
@ -267,7 +267,7 @@
|
|||
<string name="update">Cập nhật</string>
|
||||
<string name="watch_quality_pref">Chất lượng xem ưu tiên (WiFi)</string>
|
||||
<string name="limit_title">Kí tự tối đa trên tiêu đề</string>
|
||||
<string name="limit_title_rez">Độ phân giải trình phát video</string>
|
||||
<string name="limit_title_rez">Nội dung trình phát video</string>
|
||||
<string name="video_buffer_size_settings">Kích thước bộ nhớ đệm video</string>
|
||||
<string name="video_buffer_length_settings">Thời lượng bộ nhớ đệm</string>
|
||||
<string name="video_buffer_disk_settings">Lưu bộ nhớ đệm video trên ổ cứng</string>
|
||||
|
@ -380,8 +380,8 @@
|
|||
<string name="quality_webrip">Web</string>
|
||||
<string name="poster_image">Ảnh áp phích</string>
|
||||
<string name="category_player">Trình phát</string>
|
||||
<string name="resolution_and_title">Độ phân giải và Tiêu đề</string>
|
||||
<string name="title">Tiêu đề</string>
|
||||
<string name="resolution_and_title">Độ phân giải và Tên nguồn</string>
|
||||
<string name="title">Tên nguồn</string>
|
||||
<string name="resolution">Độ phân giải</string>
|
||||
<string name="error_invalid_id">Id không hợp lệ</string>
|
||||
<string name="error_invalid_data">Lỗi dữ liệu</string>
|
||||
|
@ -561,4 +561,11 @@
|
|||
\n
|
||||
\nLƯU Ý: Nếu tổng là 10 hoặc nhiều hơn, trình phát sẽ tự động bỏ tải khi liên kết đó được tải!</string>
|
||||
<string name="qualities">Các phẩm chất</string>
|
||||
<string name="already_voted">Bạn đã bình chọn</string>
|
||||
<string name="disable">Vô hiệu hoá</string>
|
||||
<string name="no_repository_found_error">Không tìm thấy tiện ích, hãy kiểm tra URL và thử VPN</string>
|
||||
<string name="no_plugins_found_error">Không tìm thấy plugin</string>
|
||||
<string name="unable_to_inflate">Không thể khởi tạo UI, đây là một LỖI LỚN và cần được báo cáo ngay lập tức tới %s</string>
|
||||
<string name="automatic_plugin_download_mode_title">Chọn chế độ để lọc plugin tải xuống</string>
|
||||
<string name="default_account">@string/default_subtitles</string>
|
||||
</resources>
|
||||
|
|
1
fastlane/metadata/android/ar-SA/changelogs/2.txt
Normal file
1
fastlane/metadata/android/ar-SA/changelogs/2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
تمت إضافة سجل التغيير!
|
|
@ -1,14 +1,10 @@
|
|||
يتيح لك كلاود ستريم -3 بث وتنزيل الأفلام والمسلسلات التلفزيونية والأنيمي. يأتي التطبيق بدون أي إعلانات وتحليلات. و يدعم العديد من مواقع البث الاولي(التريلر) والأفلام والمزيد. وتشمل الميزات:
|
||||
|
||||
يسمح لك كلاود ستريم -3 ببث وتنزيل الأفلام, المسلسلات التلفزيونية, والأنيمي.
|
||||
|
||||
يأتي التطبيق بدون أي إعلانات وتحليلات و
|
||||
يدعم العديد من مواقع البث الاولي(التريلر) ,والأفلام, والمزيد.
|
||||
|
||||
إشارات مرجعية
|
||||
|
||||
|
||||
قم بتنزيل ودفق الأفلام والبرامج التلفزيونية والأنيمي
|
||||
|
||||
|
||||
تنزيلات الترجمة
|
||||
|
||||
|
||||
دعم كروم كاست
|
||||
|
|
|
@ -1 +1 @@
|
|||
بث وتحميل الأفلام والأنمي والمسلسلات التلفزيونية.
|
||||
بث وتحميل الأفلام, الأنمي, والمسلسلات التلفزيونية.
|
||||
|
|
|
@ -1 +1 @@
|
|||
كلاود ستريم
|
||||
كلاودستريم
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Mit CloudStream-3 kannst du Filme, TV-Serien und Anime streamen und herunterladen.
|
||||
|
||||
Die App kommt ganz ohne Werbung und Analytik aus.
|
||||
Sie unterstützt mehrere Trailer-, Filmseiten und vieles mehr. Integrierte Features:
|
||||
Sie unterstützt zahlreiche Trailer, Filmseiten und vieles mehr, unter anderem:
|
||||
|
||||
Lesezeichen
|
||||
|
||||
Herunterladen und Streamen von Filmen, Fernsehsendungen und Animes
|
||||
Herunterladen und Streaming von Filmen, Fernsehsendungen und Animes
|
||||
|
||||
Downloads von Untertiteln
|
||||
|
||||
|
|
1
fastlane/metadata/android/pt/changelogs/2.txt
Normal file
1
fastlane/metadata/android/pt/changelogs/2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
- Adicionado o registo de alterações!
|
10
fastlane/metadata/android/pt/full_description.txt
Normal file
10
fastlane/metadata/android/pt/full_description.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
O CloudStream-3 permite-lhe transmitir e descarregar filmes, séries de TV e anime.
|
||||
|
||||
A aplicação é fornecida sem quaisquer anúncios e análises e
|
||||
suporta vários sites de trailers e filmes, e muito mais, por exemplo
|
||||
|
||||
Marcadores
|
||||
|
||||
Downloads de legendas
|
||||
|
||||
Suporte para Chromecast
|
1
fastlane/metadata/android/pt/short_description.txt
Normal file
1
fastlane/metadata/android/pt/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Transmita e transfira filmes, séries de TV e anime.
|
1
fastlane/metadata/android/pt/title.txt
Normal file
1
fastlane/metadata/android/pt/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
CloudStream
|
1
fastlane/metadata/android/vi/changelogs/2.txt
Normal file
1
fastlane/metadata/android/vi/changelogs/2.txt
Normal file
|
@ -0,0 +1 @@
|
|||
- Đã thêm Nhật ký thay đổi!
|
10
fastlane/metadata/android/vi/full_description.txt
Normal file
10
fastlane/metadata/android/vi/full_description.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
CloudStream-3 cho phép bạn xem và tải xuống phim lẻ, phim bộ và anime.
|
||||
|
||||
Ứng dụng không có quảng cáo hay và phân tích nào,
|
||||
đồng thời hỗ trợ nhiều trang web xem phim, v.v.
|
||||
|
||||
Đánh dấu
|
||||
|
||||
Tải phụ đề
|
||||
|
||||
Hỗ trợ Chromecast
|
1
fastlane/metadata/android/vi/short_description.txt
Normal file
1
fastlane/metadata/android/vi/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Xem và tải xuống phim lẻ, phim bộ và anime.
|
1
fastlane/metadata/android/vi/title.txt
Normal file
1
fastlane/metadata/android/vi/title.txt
Normal file
|
@ -0,0 +1 @@
|
|||
CloudStream
|
Loading…
Add table
Add a link
Reference in a new issue