Merge branch 'master' into master

This commit is contained in:
self-similarity 2023-08-28 18:07:01 +00:00 committed by GitHub
commit 3d398f0b92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 2884 additions and 1790 deletions

6
app/CMakeLists.txt Normal file
View 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})

View file

@ -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() ?: "")
@ -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:
@ -226,12 +233,12 @@ dependencies {
// 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

View file

@ -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" /> &lt;!&ndash; Used for getting if vlc is installed &ndash;&gt; -->
<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"

View 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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,12 +10,18 @@ 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()
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(
@ -27,7 +33,16 @@ open class Mp4Upload : ExtractorApi() {
)
)
}
}
srcRegex2.find(unpackedText)?.groupValues?.get(1)?.let { link ->
return listOf(
ExtractorLink(
name,
name,
link,
url,
quality ?: Qualities.Unknown.value,
)
)
}
return null
}

View file

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

View file

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

View file

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

View file

@ -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,9 +101,11 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
open fun setProgress(downloadedBytes: Long, totalBytes: Long) {
isZeroBytes = downloadedBytes == 0L
progressBar.post {
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()
val animation = ProgressBarAnimation(
@ -134,6 +136,7 @@ abstract class BaseFetchButton(context: Context, attributeSet: AttributeSet) :
progressBar.startAnimation(animation)
}
}
fun downloadStatusEvent(data: Pair<Int, VideoDownloadManager.DownloadType>) {
val (id, status) = data

View file

@ -21,7 +21,7 @@ class DownloadButton(context: Context, attributeSet: AttributeSet) :
}
override fun setStatus(status: DownloadStatusTell?) {
super.setStatus(status)
mainText?.post {
val txt = when (status) {
DownloadStatusTell.IsPaused -> R.string.download_paused
DownloadStatusTell.IsDownloading -> R.string.downloading
@ -30,6 +30,9 @@ class DownloadButton(context: Context, attributeSet: AttributeSet) :
}
mainText?.setText(txt)
}
super.setStatus(status)
}
override fun setDefaultClickListener(
card: VideoDownloadHelper.DownloadEpisodeCached,

View file

@ -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,8 +248,8 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
//progressBar.isVisible =
// status != null && status != DownloadStatusTell.Complete && status != DownloadStatusTell.Error
//progressBarBackground.isVisible = status != null && status != DownloadStatusTell.Complete
progressBarBackground.post {
val isPreActive = isZeroBytes && status == DownloadStatusTell.IsDownloading
if (animateWaiting && (status == DownloadStatusTell.IsPending || isPreActive)) {
val animation = AnimationUtils.loadAnimation(context, waitingAnimation)
progressBarBackground.startAnimation(animation)
@ -276,6 +276,7 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) :
progressBarBackground.isGone = hide
progressBar.isGone = hide
}
}
override fun resetView() {
setStatus(null)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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)
val first = listOf(defaultDir)
(try {
val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second }
val currentDir = ctx.getBasePath().let { it.first?.filePath() ?: it.second }
(first +
requireContext().getExternalFilesDirs("").mapNotNull { it.path } +
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"),

View file

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

View file

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

View file

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

View file

@ -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,16 +27,21 @@ 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)
applicationContext.getKey<VideoDownloadManager.DownloadResumePackage>(
WORK_KEY_PACKAGE,
key
)
if (info != null) {
getDownloadResumePackage(applicationContext, info.ep.id)?.let { dpkg ->
downloadFromResume(applicationContext, dpkg, ::handleNotification)
} ?: run {
downloadEpisode(
applicationContext,
info.source,
@ -43,10 +50,9 @@ class DownloadFileWorkManager(val context: Context, private val workerParams: Wo
info.links,
::handleNotification
)
awaitDownload(info.ep.id)
}
} 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
}
}

View file

@ -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,8 +20,33 @@ class M3u8Helper {
headers: Map<String, String> = mapOf(),
name: String = source
): List<ExtractorLink> {
return generator.m3u8Generation(
M3u8Stream(
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,
@ -39,7 +64,6 @@ class M3u8Helper {
)
}
}
}
private val ENCRYPTION_DETECTION_REGEX = Regex("#EXT-X-KEY:METHOD=([^,]+),")
private val ENCRYPTION_URL_IV_REGEX =
@ -47,7 +71,8 @@ class M3u8Helper {
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
while (true) {
yield(toBytes16Big(initial))
++initial
private fun defaultIv(index: Int) : ByteArray {
return toBytes16Big(index+1)
}
}.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) {
?: throw IllegalArgumentException("qualities has no streams")
val m3u8Response =
runBlocking {
app.get(
secondSelection.streamUrl,
headers = headers,
verify = false
).text
}
var encryptionUri: String?
// encryption, this is because crunchy uses it
var encryptionIv = byteArrayOf()
var encryptionData = byteArrayOf()
val encryptionState = isEncrypted(m3u8Response)
if (encryptionState) {
// its safe to assume that its not going to be null
val match =
ENCRYPTION_URL_IV_REGEX.find(m3u8Response)!!.destructured // its safe to assume that its not going to be null
encryptionUri = match.component2()
ENCRYPTION_URL_IV_REGEX.find(m3u8Response)!!.groupValues
var encryptionUri = match[2]
if (isNotCompleteUrl(encryptionUri)) {
encryptionUri = "${getParentLink(secondSelection.streamUrl)}/$encryptionUri"
}
encryptionIv = match.component3().toByteArray()
val encryptionKeyResponse =
runBlocking { app.get(encryptionUri, headers = headers, verify = false) }
encryptionData = encryptionKeyResponse.body?.bytes() ?: byteArrayOf()
encryptionIv = match[3].toByteArray()
val encryptionKeyResponse = app.get(encryptionUri, headers = headers, verify = false)
encryptionData = encryptionKeyResponse.body.bytes()
}
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
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")
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()
}
return listOf(HlsDownloadData(byteArrayOf(), 1, 1, true)).iterator()
return LazyHlsDownloadData(
encryptionData = encryptionData,
encryptionIv = encryptionIv,
isEncrypted = encryptionState,
allTsLinks = allTsList,
relativeUrl = relativeUrl,
headers = headers
)
}
}

View file

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

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

View file

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

View file

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

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources/>

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

View file

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

View file

@ -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-&gt;%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>

View file

@ -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 =&gt; 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 =&gt; 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>

View file

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

View file

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

View file

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

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources/>

View file

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

View file

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

View file

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

View file

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

View 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 -&gt; %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>

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -0,0 +1 @@
تمت إضافة سجل التغيير!

View file

@ -1,14 +1,10 @@
يتيح لك كلاود ستريم -3 بث وتنزيل الأفلام والمسلسلات التلفزيونية والأنيمي. يأتي التطبيق بدون أي إعلانات وتحليلات. و يدعم العديد من مواقع البث الاولي(التريلر) والأفلام والمزيد. وتشمل الميزات:
يسمح لك كلاود ستريم -3 ببث وتنزيل الأفلام, المسلسلات التلفزيونية, والأنيمي.
يأتي التطبيق بدون أي إعلانات وتحليلات و
يدعم العديد من مواقع البث الاولي(التريلر) ,والأفلام, والمزيد.
إشارات مرجعية
قم بتنزيل ودفق الأفلام والبرامج التلفزيونية والأنيمي
تنزيلات الترجمة
دعم كروم كاست

View file

@ -1 +1 @@
بث وتحميل الأفلام والأنمي والمسلسلات التلفزيونية.
بث وتحميل الأفلام, الأنمي, والمسلسلات التلفزيونية.

View file

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

View file

@ -0,0 +1 @@
- Adicionado o registo de alterações!

View 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

View file

@ -0,0 +1 @@
Transmita e transfira filmes, séries de TV e anime.

View file

@ -0,0 +1 @@
CloudStream

View file

@ -0,0 +1 @@
- Đã thêm Nhật ký thay đổi!

View 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

View file

@ -0,0 +1 @@
Xem và tải xuống phim lẻ, phim bộ và anime.

View file

@ -0,0 +1 @@
CloudStream