Merge pull request #2 from allenbaby/HEAD

Updating
This commit is contained in:
Allen Baby 2023-02-24 22:56:34 +05:30 committed by GitHub
commit 35a99530d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 1127 additions and 112 deletions

View file

@ -184,8 +184,8 @@ dependencies {
//implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0") //implementation("com.github.TorrentStream:TorrentStream-Android:2.7.0")
// Downloading // Downloading
implementation("androidx.work:work-runtime:2.7.1") implementation("androidx.work:work-runtime:2.8.0")
implementation("androidx.work:work-runtime-ktx:2.7.1") implementation("androidx.work:work-runtime-ktx:2.8.0")
// Networking // Networking
// implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp:4.9.2")

View file

@ -1327,7 +1327,7 @@ fun LoadResponse?.isAnimeBased(): Boolean {
fun TvType?.isEpisodeBased(): Boolean { fun TvType?.isEpisodeBased(): Boolean {
if (this == null) return false if (this == null) return false
return (this == TvType.TvSeries || this == TvType.Anime) return (this == TvType.TvSeries || this == TvType.Anime || this == TvType.AsianDrama)
} }
@ -1351,6 +1351,7 @@ interface EpisodeResponse {
var showStatus: ShowStatus? var showStatus: ShowStatus?
var nextAiring: NextAiring? var nextAiring: NextAiring?
var seasonNames: List<SeasonData>? var seasonNames: List<SeasonData>?
fun getLatestEpisodes(): Map<DubStatus, Int?>
} }
@JvmName("addSeasonNamesString") @JvmName("addSeasonNamesString")
@ -1419,7 +1420,18 @@ data class AnimeLoadResponse(
override var nextAiring: NextAiring? = null, override var nextAiring: NextAiring? = null,
override var seasonNames: List<SeasonData>? = null, override var seasonNames: List<SeasonData>? = null,
override var backgroundPosterUrl: String? = null, override var backgroundPosterUrl: String? = null,
) : LoadResponse, EpisodeResponse ) : LoadResponse, EpisodeResponse {
override fun getLatestEpisodes(): Map<DubStatus, Int?> {
return episodes.map { (status, episodes) ->
val maxSeason = episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
status to episodes
.filter { it.season == maxSeason }
.maxOfOrNull { it.episode ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
}.toMap()
}
}
/** /**
* If episodes already exist appends the list. * If episodes already exist appends the list.
@ -1617,7 +1629,17 @@ data class TvSeriesLoadResponse(
override var nextAiring: NextAiring? = null, override var nextAiring: NextAiring? = null,
override var seasonNames: List<SeasonData>? = null, override var seasonNames: List<SeasonData>? = null,
override var backgroundPosterUrl: String? = null, override var backgroundPosterUrl: String? = null,
) : LoadResponse, EpisodeResponse ) : LoadResponse, EpisodeResponse {
override fun getLatestEpisodes(): Map<DubStatus, Int?> {
val maxSeason =
episodes.maxOfOrNull { it.season ?: Int.MIN_VALUE }.takeUnless { it == Int.MIN_VALUE }
val max = episodes
.filter { it.season == maxSeason }
.maxOfOrNull { it.episode ?: Int.MIN_VALUE }
.takeUnless { it == Int.MIN_VALUE }
return mapOf(DubStatus.None to max)
}
}
suspend fun MainAPI.newTvSeriesLoadResponse( suspend fun MainAPI.newTvSeriesLoadResponse(
name: String, name: String,

View file

@ -32,7 +32,9 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.google.android.gms.cast.framework.* import com.google.android.gms.cast.framework.*
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.navigationrail.NavigationRailView import com.google.android.material.navigationrail.NavigationRailView
import com.google.android.material.snackbar.Snackbar
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.APIHolder.allProviders import com.lagradost.cloudstream3.APIHolder.allProviders
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
@ -79,6 +81,7 @@ import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions
import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.*
import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.html
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.isNetworkAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.loadRepository import com.lagradost.cloudstream3.utils.AppUtils.loadRepository
import com.lagradost.cloudstream3.utils.AppUtils.loadResult import com.lagradost.cloudstream3.utils.AppUtils.loadResult
@ -86,6 +89,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe 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.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStore.setKey
import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching
@ -717,6 +721,28 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
changeStatusBarState(isEmulatorSettings()) changeStatusBarState(isEmulatorSettings())
// Automatically enable jsdelivr if cant connect to raw.githubusercontent.com
if (this.getKey<Boolean>(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) {
main {
if (checkGithubConnectivity()) {
this.setKey(getString(R.string.jsdelivr_proxy_key), false)
} else {
this.setKey(getString(R.string.jsdelivr_proxy_key), true)
val parentView: View = findViewById(android.R.id.content)
Snackbar.make(parentView, R.string.jsdelivr_enabled, Snackbar.LENGTH_LONG).let { snackbar ->
snackbar.setAction(R.string.revert) {
setKey(getString(R.string.jsdelivr_proxy_key), false)
}
snackbar.setBackgroundTint(colorFromAttribute(R.attr.primaryGrayBackground))
snackbar.setTextColor(colorFromAttribute(R.attr.textColor))
snackbar.setActionTextColor(colorFromAttribute(R.attr.colorPrimary))
snackbar.show()
}
}
}
}
if (PluginManager.checkSafeModeFile()) { if (PluginManager.checkSafeModeFile()) {
normalSafeApiCall { normalSafeApiCall {
@ -1090,4 +1116,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
// } // }
} }
suspend fun checkGithubConnectivity(): Boolean {
return try {
app.get("https://raw.githubusercontent.com/recloudstream/.github/master/connectivitycheck", timeout = 5).text.trim() == "ok"
} catch (t: Throwable) {
false
}
}
} }

View file

@ -130,7 +130,7 @@ open class StreamSB : ExtractorApi() {
it.value.replace(Regex("(embed-|/e/)"), "") it.value.replace(Regex("(embed-|/e/)"), "")
}.first() }.first()
// val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362" // val master = "$mainUrl/sources48/6d6144797752744a454267617c7c${bytesToHex.lowercase()}7c7c4e61755a56456f34385243727c7c73747265616d7362/6b4a33767968506e4e71374f7c7c343837323439333133333462353935333633373836643638376337633462333634663539343137373761333635313533333835333763376333393636363133393635366136323733343435323332376137633763373337343732363536313664373336327c7c504d754478413835306633797c7c73747265616d7362"
val master = "$mainUrl/sources50/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/" val master = "$mainUrl/sources51/" + bytesToHex("||$id||||streamsb".toByteArray()) + "/"
val headers = mapOf( val headers = mapOf(
"watchsb" to "sbstream", "watchsb" to "sbstream",
) )

View file

@ -16,6 +16,7 @@ import com.google.gson.Gson
import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings
import com.lagradost.cloudstream3.APIHolder.removePluginMapping import com.lagradost.cloudstream3.APIHolder.removePluginMapping
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
@ -165,11 +166,11 @@ object PluginManager {
private var loadedLocalPlugins = false private var loadedLocalPlugins = false
private val gson = Gson() private val gson = Gson()
private suspend fun maybeLoadPlugin(activity: Activity, file: File) { private suspend fun maybeLoadPlugin(context: Context, file: File) {
val name = file.name val name = file.name
if (file.extension == "zip" || file.extension == "cs3") { if (file.extension == "zip" || file.extension == "cs3") {
loadPlugin( loadPlugin(
activity, context,
file, file,
PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET) PluginData(name, null, false, file.absolutePath, PLUGIN_VERSION_NOT_SET)
) )
@ -199,7 +200,7 @@ object PluginManager {
// var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet() // var allCurrentOutDatedPlugins: Set<OnlinePluginData> = emptySet()
suspend fun loadSinglePlugin(activity: Activity, apiName: String): Boolean { suspend fun loadSinglePlugin(context: Context, apiName: String): Boolean {
return (getPluginsOnline().firstOrNull { return (getPluginsOnline().firstOrNull {
// Most of the time the provider ends with Provider which isn't part of the api name // Most of the time the provider ends with Provider which isn't part of the api name
it.internalName.replace("provider", "", ignoreCase = true) == apiName it.internalName.replace("provider", "", ignoreCase = true) == apiName
@ -209,7 +210,7 @@ object PluginManager {
})?.let { savedData -> })?.let { savedData ->
// OnlinePluginData(savedData, onlineData) // OnlinePluginData(savedData, onlineData)
loadPlugin( loadPlugin(
activity, context,
File(savedData.filePath), File(savedData.filePath),
savedData savedData
) )
@ -371,11 +372,11 @@ object PluginManager {
/** /**
* Use updateAllOnlinePluginsAndLoadThem * Use updateAllOnlinePluginsAndLoadThem
* */ * */
fun loadAllOnlinePlugins(activity: Activity) { fun loadAllOnlinePlugins(context: Context) {
// Load all plugins as fast as possible! // Load all plugins as fast as possible!
(getPluginsOnline()).toList().apmap { pluginData -> (getPluginsOnline()).toList().apmap { pluginData ->
loadPlugin( loadPlugin(
activity, context,
File(pluginData.filePath), File(pluginData.filePath),
pluginData pluginData
) )
@ -398,7 +399,7 @@ object PluginManager {
* @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins * @param forceReload see afterPluginsLoadedEvent, basically a way to load all local plugins
* and reload all pages even if they are previously valid * and reload all pages even if they are previously valid
**/ **/
fun loadAllLocalPlugins(activity: Activity, forceReload: Boolean) { fun loadAllLocalPlugins(context: Context, forceReload: Boolean) {
val dir = File(LOCAL_PLUGINS_PATH) val dir = File(LOCAL_PLUGINS_PATH)
removeKey(PLUGINS_KEY_LOCAL) removeKey(PLUGINS_KEY_LOCAL)
@ -416,7 +417,7 @@ object PluginManager {
Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: $sortedPlugins") Log.d(TAG, "Files in '${LOCAL_PLUGINS_PATH}' folder: $sortedPlugins")
sortedPlugins?.sortedBy { it.name }?.apmap { file -> sortedPlugins?.sortedBy { it.name }?.apmap { file ->
maybeLoadPlugin(activity, file) maybeLoadPlugin(context, file)
} }
loadedLocalPlugins = true loadedLocalPlugins = true
@ -441,14 +442,14 @@ object PluginManager {
/** /**
* @return True if successful, false if not * @return True if successful, false if not
* */ * */
private suspend fun loadPlugin(activity: Activity, file: File, data: PluginData): Boolean { private suspend fun loadPlugin(context: Context, file: File, data: PluginData): Boolean {
val fileName = file.nameWithoutExtension val fileName = file.nameWithoutExtension
val filePath = file.absolutePath val filePath = file.absolutePath
currentlyLoading = fileName currentlyLoading = fileName
Log.i(TAG, "Loading plugin: $data") Log.i(TAG, "Loading plugin: $data")
return try { return try {
val loader = PathClassLoader(filePath, activity.classLoader) val loader = PathClassLoader(filePath, context.classLoader)
var manifest: Plugin.Manifest var manifest: Plugin.Manifest
loader.getResourceAsStream("manifest.json").use { stream -> loader.getResourceAsStream("manifest.json").use { stream ->
if (stream == null) { if (stream == null) {
@ -492,22 +493,22 @@ object PluginManager {
addAssetPath.invoke(assets, file.absolutePath) addAssetPath.invoke(assets, file.absolutePath)
pluginInstance.resources = Resources( pluginInstance.resources = Resources(
assets, assets,
activity.resources.displayMetrics, context.resources.displayMetrics,
activity.resources.configuration context.resources.configuration
) )
} }
plugins[filePath] = pluginInstance plugins[filePath] = pluginInstance
classLoaders[loader] = pluginInstance classLoaders[loader] = pluginInstance
urlPlugins[data.url ?: filePath] = pluginInstance urlPlugins[data.url ?: filePath] = pluginInstance
pluginInstance.load(activity) pluginInstance.load(context)
Log.i(TAG, "Loaded plugin ${data.internalName} successfully") Log.i(TAG, "Loaded plugin ${data.internalName} successfully")
currentlyLoading = null currentlyLoading = null
true true
} catch (e: Throwable) { } catch (e: Throwable) {
Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}") Log.e(TAG, "Failed to load $file: ${Log.getStackTraceString(e)}")
showToast( showToast(
activity, context.getActivity(),
activity.getString(R.string.plugin_load_fail).format(fileName), context.getString(R.string.plugin_load_fail).format(fileName),
Toast.LENGTH_LONG Toast.LENGTH_LONG
) )
currentlyLoading = null currentlyLoading = null

View file

@ -2,8 +2,10 @@ package com.lagradost.cloudstream3.plugins
import android.content.Context import android.content.Context
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication.Companion.context
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.amap import com.lagradost.cloudstream3.amap
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
@ -71,6 +73,15 @@ object RepositoryManager {
val PREBUILT_REPOSITORIES: Array<RepositoryData> by lazy { val PREBUILT_REPOSITORIES: Array<RepositoryData> by lazy {
getKey("PREBUILT_REPOSITORIES") ?: emptyArray() getKey("PREBUILT_REPOSITORIES") ?: emptyArray()
} }
val GH_REGEX = Regex("^https://raw.githubusercontent.com/([A-Za-z0-9-]+)/([A-Za-z0-9_.-]+)/(.*)$")
/* Convert raw.githubusercontent.com urls to cdn.jsdelivr.net if enabled in settings */
fun convertRawGitUrl(url: String): String {
if (getKey<Boolean>(context!!.getString(R.string.jsdelivr_proxy_key)) != true) return url
val match = GH_REGEX.find(url) ?: return url
val (user, repo, rest) = match.destructured
return "https://cdn.jsdelivr.net/gh/$user/$repo@$rest"
}
suspend fun parseRepoUrl(url: String): String? { suspend fun parseRepoUrl(url: String): String? {
val fixedUrl = url.trim() val fixedUrl = url.trim()
@ -84,10 +95,15 @@ object RepositoryManager {
} }
} else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) { } else if (fixedUrl.matches("^[a-zA-Z0-9!_-]+$".toRegex())) {
suspendSafeApiCall { suspendSafeApiCall {
app.get("https://l.cloudstream.cf/${fixedUrl}").let { app.get("https://l.cloudstream.cf/${fixedUrl}", allowRedirects = false).let {
return@let if (it.isSuccessful && !it.url.startsWith("https://cutt.ly/branded-domains")) it.url it.headers["Location"]?.let { url ->
else app.get("https://cutt.ly/${fixedUrl}").let let2@{ it2 -> return@suspendSafeApiCall if (!url.startsWith("https://cutt.ly/branded-domains")) url
return@let2 if (it2.isSuccessful) it2.url else null else null
}
app.get("https://cutt.ly/${fixedUrl}", allowRedirects = false).let { it2 ->
it2.headers["Location"]?.let { url ->
return@suspendSafeApiCall if (url.startsWith("https://cutt.ly/404")) url else null
}
} }
} }
} }
@ -97,14 +113,14 @@ object RepositoryManager {
suspend fun parseRepository(url: String): Repository? { suspend fun parseRepository(url: String): Repository? {
return suspendSafeApiCall { return suspendSafeApiCall {
// Take manifestVersion and such into account later // Take manifestVersion and such into account later
app.get(url).parsedSafe() app.get(convertRawGitUrl(url)).parsedSafe()
} }
} }
private suspend fun parsePlugins(pluginUrls: String): List<SitePlugin> { private suspend fun parsePlugins(pluginUrls: String): List<SitePlugin> {
// Take manifestVersion and such into account later // Take manifestVersion and such into account later
return try { return try {
val response = app.get(pluginUrls) val response = app.get(convertRawGitUrl(pluginUrls))
// Normal parsed function not working? // Normal parsed function not working?
// return response.parsedSafe() // return response.parsedSafe()
tryParseJson<Array<SitePlugin>>(response.text)?.toList() ?: emptyList() tryParseJson<Array<SitePlugin>>(response.text)?.toList() ?: emptyList()
@ -139,7 +155,7 @@ object RepositoryManager {
} }
file.createNewFile() file.createNewFile()
val body = app.get(pluginUrl).okhttpResponse.body val body = app.get(convertRawGitUrl(pluginUrl)).okhttpResponse.body
write(body.byteStream(), file.outputStream()) write(body.byteStream(), file.outputStream())
file file
} }

View file

@ -0,0 +1,218 @@
package com.lagradost.cloudstream3.services
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.net.toUri
import androidx.work.*
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.mvvm.safeApiCall
import com.lagradost.cloudstream3.plugins.PluginManager
import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDub
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import com.lagradost.cloudstream3.utils.VideoDownloadManager.getImageBitmapFromUrl
import kotlinx.coroutines.withTimeoutOrNull
import java.util.concurrent.TimeUnit
const val SUBSCRIPTION_CHANNEL_ID = "cloudstream3.subscriptions"
const val SUBSCRIPTION_WORK_NAME = "work_subscription"
const val SUBSCRIPTION_CHANNEL_NAME = "Subscriptions"
const val SUBSCRIPTION_CHANNEL_DESCRIPTION = "Notifications for new episodes on subscribed shows"
const val SUBSCRIPTION_NOTIFICATION_ID = 938712897 // Random unique
class SubscriptionWorkManager(val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
companion object {
fun enqueuePeriodicWork(context: Context?) {
if (context == null) return
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val periodicSyncDataWork =
PeriodicWorkRequest.Builder(SubscriptionWorkManager::class.java, 6, TimeUnit.HOURS)
.addTag(SUBSCRIPTION_WORK_NAME)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
SUBSCRIPTION_WORK_NAME,
ExistingPeriodicWorkPolicy.KEEP,
periodicSyncDataWork
)
// Uncomment below for testing
// val oneTimeSyncDataWork =
// OneTimeWorkRequest.Builder(SubscriptionWorkManager::class.java)
// .addTag(SUBSCRIPTION_WORK_NAME)
// .setConstraints(constraints)
// .build()
//
// WorkManager.getInstance(context).enqueue(oneTimeSyncDataWork)
}
}
private val progressNotificationBuilder =
NotificationCompat.Builder(context, SUBSCRIPTION_CHANNEL_ID)
.setAutoCancel(false)
.setColorized(true)
.setOnlyAlertOnce(true)
.setSilent(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setContentTitle(context.getString(R.string.subscription_in_progress_notification))
.setSmallIcon(R.drawable.quantum_ic_refresh_white_24)
.setProgress(0, 0, true)
private val updateNotificationBuilder =
NotificationCompat.Builder(context, SUBSCRIPTION_CHANNEL_ID)
.setColorized(true)
.setOnlyAlertOnce(true)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setColor(context.colorFromAttribute(R.attr.colorPrimary))
.setSmallIcon(R.drawable.ic_cloudstream_monochrome_big)
private val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private fun updateProgress(max: Int, progress: Int, indeterminate: Boolean) {
notificationManager.notify(
SUBSCRIPTION_NOTIFICATION_ID, progressNotificationBuilder
.setProgress(max, progress, indeterminate)
.build()
)
}
override suspend fun doWork(): Result {
// println("Update subscriptions!")
context.createNotificationChannel(
SUBSCRIPTION_CHANNEL_ID,
SUBSCRIPTION_CHANNEL_NAME,
SUBSCRIPTION_CHANNEL_DESCRIPTION
)
safeApiCall {
setForeground(
ForegroundInfo(
SUBSCRIPTION_NOTIFICATION_ID,
progressNotificationBuilder.build()
)
)
}
val subscriptions = getAllSubscriptions()
if (subscriptions.isEmpty()) {
WorkManager.getInstance(context).cancelWorkById(this.id)
return Result.success()
}
val max = subscriptions.size
var progress = 0
updateProgress(max, progress, true)
// We need all plugins loaded.
PluginManager.loadAllOnlinePlugins(context)
PluginManager.loadAllLocalPlugins(context, false)
subscriptions.apmap { savedData ->
try {
val id = savedData.id ?: return@apmap null
val api = getApiFromNameNull(savedData.apiName) ?: return@apmap null
// Reasonable timeout to prevent having this worker run forever.
val response = withTimeoutOrNull(60_000) {
api.load(savedData.url) as? EpisodeResponse
} ?: return@apmap null
val dubPreference =
getDub(id) ?: if (
context.getApiDubstatusSettings().contains(DubStatus.Dubbed)
) {
DubStatus.Dubbed
} else {
DubStatus.Subbed
}
val latestEpisodes = response.getLatestEpisodes()
val latestPreferredEpisode = latestEpisodes[dubPreference]
val (shouldUpdate, latestEpisode) = if (latestPreferredEpisode != null) {
val latestSeenEpisode =
savedData.lastSeenEpisodeCount[dubPreference] ?: Int.MIN_VALUE
val shouldUpdate = latestPreferredEpisode > latestSeenEpisode
shouldUpdate to latestPreferredEpisode
} else {
val latestEpisode = latestEpisodes[DubStatus.None] ?: Int.MIN_VALUE
val latestSeenEpisode =
savedData.lastSeenEpisodeCount[DubStatus.None] ?: Int.MIN_VALUE
val shouldUpdate = latestEpisode > latestSeenEpisode
shouldUpdate to latestEpisode
}
DataStoreHelper.updateSubscribedData(
id,
savedData,
response
)
if (shouldUpdate) {
val updateHeader = savedData.name
val updateDescription = txt(
R.string.subscription_episode_released,
latestEpisode,
savedData.name
).asString(context)
val intent = Intent(context, MainActivity::class.java).apply {
data = savedData.url.toUri()
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pendingIntent =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE
)
} else {
PendingIntent.getActivity(context, 0, intent, 0)
}
val poster = ioWork { savedData.posterUrl?.let { url -> context.getImageBitmapFromUrl(url, savedData.posterHeaders) } }
val updateNotification =
updateNotificationBuilder.setContentTitle(updateHeader)
.setContentText(updateDescription)
.setContentIntent(pendingIntent)
.setLargeIcon(poster)
.build()
notificationManager.notify(id, updateNotification)
}
// You can probably get some issues here since this is async but it does not matter much.
updateProgress(max, ++progress, false)
} catch (_: Throwable) {
}
}
return Result.success()
}
}

View file

@ -9,6 +9,7 @@ import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.library.ListSorting import com.lagradost.cloudstream3.ui.library.ListSorting
import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.result.txt
import com.lagradost.cloudstream3.utils.Coroutines.ioWork import com.lagradost.cloudstream3.utils.Coroutines.ioWork
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllSubscriptions
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds
import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData
import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState
@ -74,13 +75,16 @@ class LocalList : SyncAPI {
group.value.mapNotNull { group.value.mapNotNull {
getBookmarkedData(it.first)?.toLibraryItem(it.first.toString()) getBookmarkedData(it.first)?.toLibraryItem(it.first.toString())
} }
} } + mapOf(R.string.subscription_list_name to getAllSubscriptions().mapNotNull {
it.toLibraryItem()
})
} }
val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate {
// None is not something to display // None is not something to display
it.stringRes to emptyList<SyncAPI.LibraryItem>() it.stringRes to emptyList<SyncAPI.LibraryItem>()
} } + mapOf(R.string.subscription_list_name to emptyList())
return SyncAPI.LibraryMetadata( return SyncAPI.LibraryMetadata(
(baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) },
setOf( setOf(

View file

@ -15,6 +15,7 @@ import android.view.ViewGroup
import android.widget.AbsListView import android.widget.AbsListView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -27,12 +28,14 @@ import com.google.android.material.chip.ChipDrawable
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.mvvm.*
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
@ -904,6 +907,36 @@ open class ResultFragment : ResultTrailerPlayer() {
updateList(d.actors ?: emptyList()) updateList(d.actors ?: emptyList())
} }
observeNullable(viewModel.subscribeStatus) { isSubscribed ->
result_subscribe?.isVisible = isSubscribed != null
if (isSubscribed == null) return@observeNullable
val drawable = if (isSubscribed) {
R.drawable.ic_baseline_notifications_active_24
} else {
R.drawable.baseline_notifications_none_24
}
result_subscribe?.setImageResource(drawable)
}
result_subscribe?.setOnClickListener {
val isSubscribed =
viewModel.toggleSubscriptionStatus() ?: return@setOnClickListener
val message = if (isSubscribed) {
// Kinda icky to have this here, but it works.
SubscriptionWorkManager.enqueuePeriodicWork(context)
R.string.subscription_new
} else {
R.string.subscription_deleted
}
val name = (viewModel.page.value as? Resource.Success)?.value?.title
?: txt(R.string.no_data).asStringNull(context) ?: ""
showToast(activity, txt(message, name), Toast.LENGTH_SHORT)
}
result_open_in_browser?.isVisible = d.url.startsWith("http") result_open_in_browser?.isVisible = d.url.startsWith("http")
result_open_in_browser?.setOnClickListener { result_open_in_browser?.setOnClickListener {
val i = Intent(ACTION_VIEW) val i = Intent(ACTION_VIEW)

View file

@ -16,6 +16,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.getId
import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.getCastSession
import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.showToast
@ -414,6 +415,9 @@ class ResultViewModel2 : ViewModel() {
private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null) private val _episodeSynopsis: MutableLiveData<String?> = MutableLiveData(null)
val episodeSynopsis: LiveData<String?> = _episodeSynopsis val episodeSynopsis: LiveData<String?> = _episodeSynopsis
private val _subscribeStatus: MutableLiveData<Boolean?> = MutableLiveData(null)
val subscribeStatus: LiveData<Boolean?> = _subscribeStatus
companion object { companion object {
const val TAG = "RVM2" const val TAG = "RVM2"
private const val EPISODE_RANGE_SIZE = 20 private const val EPISODE_RANGE_SIZE = 20
@ -815,6 +819,42 @@ class ResultViewModel2 : ViewModel() {
} }
} }
/**
* @return true if the new status is Subscribed, false if not. Null if not possible to subscribe.
**/
fun toggleSubscriptionStatus(): Boolean? {
val isSubscribed = _subscribeStatus.value ?: return null
val response = currentResponse ?: return null
if (response !is EpisodeResponse) return null
val currentId = response.getId()
if (isSubscribed) {
DataStoreHelper.removeSubscribedData(currentId)
} else {
val current = DataStoreHelper.getSubscribedData(currentId)
DataStoreHelper.setSubscribedData(
currentId,
DataStoreHelper.SubscribedData(
currentId,
current?.bookmarkedTime ?: unixTimeMS,
unixTimeMS,
response.getLatestEpisodes(),
response.name,
response.url,
response.apiName,
response.type,
response.posterUrl,
response.year
)
)
}
_subscribeStatus.postValue(!isSubscribed)
return !isSubscribed
}
private fun startChromecast( private fun startChromecast(
activity: Activity?, activity: Activity?,
result: ResultEpisode, result: ResultEpisode,
@ -1473,7 +1513,8 @@ class ResultViewModel2 : ViewModel() {
this.engName, this.engName,
this.name, this.name,
this.japName this.japName
).filter { it.length > 2 }.distinct(), // the reason why we filter is due to not wanting smth like " " or "?" ).filter { it.length > 2 }
.distinct(), // the reason why we filter is due to not wanting smth like " " or "?"
TrackerType.getTypes(this.type), TrackerType.getTypes(this.type),
this.year this.year
) )
@ -1670,6 +1711,16 @@ class ResultViewModel2 : ViewModel() {
postResume() postResume()
} }
private fun postSubscription(loadResponse: LoadResponse) {
if (loadResponse.isEpisodeBased()) {
val id = loadResponse.getId()
val data = DataStoreHelper.getSubscribedData(id)
DataStoreHelper.updateSubscribedData(id, data, loadResponse as? EpisodeResponse)
val isSubscribed = data != null
_subscribeStatus.postValue(isSubscribed)
}
}
private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) { private fun postEpisodeRange(indexer: EpisodeIndexer?, range: EpisodeRange?) {
if (range == null || indexer == null) { if (range == null || indexer == null) {
return return
@ -1806,6 +1857,7 @@ class ResultViewModel2 : ViewModel() {
) { ) {
currentResponse = loadResponse currentResponse = loadResponse
postPage(loadResponse, apiRepository) postPage(loadResponse, apiRepository)
postSubscription(loadResponse)
if (updateEpisodes) if (updateEpisodes)
postEpisodes(loadResponse, updateFillers) postEpisodes(loadResponse, updateFillers)
} }

View file

@ -74,6 +74,7 @@ val appLanguages = arrayListOf(
Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Bahasa Indonesia", "in"),
Triple("", "italiano", "it"), Triple("", "italiano", "it"),
Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"), Triple("\uD83C\uDDEE\uD83C\uDDF1", "עברית", "iw"),
Triple("", "日本語 (にほんご)", "ja"),
Triple("", "ಕನ್ನಡ", "kn"), Triple("", "ಕನ್ನಡ", "kn"),
Triple("", "македонски", "mk"), Triple("", "македонски", "mk"),
Triple("", "മലയാളം", "ml"), Triple("", "മലയാളം", "ml"),
@ -82,7 +83,7 @@ val appLanguages = arrayListOf(
Triple("", "norsk bokmål", "no"), Triple("", "norsk bokmål", "no"),
Triple("", "polski", "pl"), Triple("", "polski", "pl"),
Triple("\uD83C\uDDF5\uD83C\uDDF9", "português", "pt"), Triple("\uD83C\uDDF5\uD83C\uDDF9", "português", "pt"),
Triple("🦍", "mmmm... monke", "qt"), Triple("\uD83E\uDD8D", "mmmm... monke", "qt"),
Triple("", "română", "ro"), Triple("", "română", "ro"),
Triple("", "русский", "ru"), Triple("", "русский", "ru"),
Triple("", "slovenčina", "sk"), Triple("", "slovenčina", "sk"),
@ -97,7 +98,7 @@ val appLanguages = arrayListOf(
Triple("", "中文", "zh"), Triple("", "中文", "zh"),
Triple("\uD83C\uDDF9\uD83C\uDDFC", "文言", "zh-rTW"), Triple("\uD83C\uDDF9\uD83C\uDDFC", "文言", "zh-rTW"),
/* end language list */ /* end language list */
).sortedBy { it.second?.toLowerCase() } //ye, we go alphabetical, so ppl don't put their lang on top ).sortedBy { it.second.lowercase() } //ye, we go alphabetical, so ppl don't put their lang on top
class SettingsGeneral : PreferenceFragmentCompat() { class SettingsGeneral : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -157,9 +158,6 @@ class SettingsGeneral : PreferenceFragmentCompat() {
getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref ->
val tempLangs = appLanguages.toMutableList() val tempLangs = appLanguages.toMutableList()
//if (beneneCount > 100) {
// tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo"))
//}
val current = getCurrentLocale(pref.context) val current = getCurrentLocale(pref.context)
val languageCodes = tempLangs.map { (_, _, iso) -> iso } val languageCodes = tempLangs.map { (_, _, iso) -> iso }
val languageNames = tempLangs.map { (emoji, name, iso) -> val languageNames = tempLangs.map { (emoji, name, iso) ->
@ -316,6 +314,12 @@ class SettingsGeneral : PreferenceFragmentCompat() {
} ?: emptyList() } ?: emptyList()
} }
settingsManager.edit().putBoolean(getString(R.string.jsdelivr_proxy_key), getKey(getString(R.string.jsdelivr_proxy_key), false) ?: false).apply()
getPref(R.string.jsdelivr_proxy_key)?.setOnPreferenceChangeListener { _, newValue ->
setKey(getString(R.string.jsdelivr_proxy_key), newValue)
return@setOnPreferenceChangeListener true
}
getPref(R.string.download_path_key)?.setOnPreferenceClickListener { getPref(R.string.download_path_key)?.setOnPreferenceClickListener {
val dirs = getDownloadDirs() val dirs = getDownloadDirs()

View file

@ -4,6 +4,8 @@ import android.animation.ObjectAnimator
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.app.Activity.RESULT_CANCELED import android.app.Activity.RESULT_CANCELED
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.* import android.content.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.database.Cursor import android.database.Cursor
@ -196,6 +198,22 @@ object AppUtils {
animation.start() animation.start()
} }
fun Context.createNotificationChannel(channelId: String, channelName: String, description: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel =
NotificationChannel(channelId, channelName, importance).apply {
this.description = description
}
// Register the channel with the system.
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun getAllWatchNextPrograms(context: Context): Set<Long> { fun getAllWatchNextPrograms(context: Context): Set<Long> {
val COLUMN_WATCH_NEXT_ID_INDEX = 0 val COLUMN_WATCH_NEXT_ID_INDEX = 0
@ -473,6 +491,12 @@ object AppUtils {
} }
} }
fun Context.isNetworkAvailable(): Boolean {
val manager = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = manager.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnected || manager.allNetworkInfo?.any { it.isConnected } ?: false
}
fun splitQuery(url: URL): Map<String, String> { fun splitQuery(url: URL): Map<String, String> {
val queryPairs: MutableMap<String, String> = LinkedHashMap() val queryPairs: MutableMap<String, String> = LinkedHashMap()
val query: String = url.query val query: String = url.query

View file

@ -1,16 +1,13 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.SearchQuality
import com.lagradost.cloudstream3.SearchResponse
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI
import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.WatchType
@ -20,6 +17,7 @@ const val VIDEO_POS_DUR = "video_pos_dur"
const val VIDEO_WATCH_STATE = "video_watch_state" const val VIDEO_WATCH_STATE = "video_watch_state"
const val RESULT_WATCH_STATE = "result_watch_state" const val RESULT_WATCH_STATE = "result_watch_state"
const val RESULT_WATCH_STATE_DATA = "result_watch_state_data" const val RESULT_WATCH_STATE_DATA = "result_watch_state_data"
const val RESULT_SUBSCRIBED_STATE_DATA = "result_subscribed_state_data"
const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes const val RESULT_RESUME_WATCHING = "result_resume_watching_2" // changed due to id changes
const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching" const val RESULT_RESUME_WATCHING_OLD = "result_resume_watching"
const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated" const val RESULT_RESUME_WATCHING_HAS_MIGRATED = "result_resume_watching_migrated"
@ -42,6 +40,37 @@ object DataStoreHelper {
return this return this
} }
/**
* Used to display notifications on new episodes and posters in library.
**/
data class SubscribedData(
@JsonProperty("id") override var id: Int?,
@JsonProperty("subscribedTime") val bookmarkedTime: Long,
@JsonProperty("latestUpdatedTime") val latestUpdatedTime: Long,
@JsonProperty("lastSeenEpisodeCount") val lastSeenEpisodeCount: Map<DubStatus, Int?>,
@JsonProperty("name") override val name: String,
@JsonProperty("url") override val url: String,
@JsonProperty("apiName") override val apiName: String,
@JsonProperty("type") override var type: TvType? = null,
@JsonProperty("posterUrl") override var posterUrl: String?,
@JsonProperty("year") val year: Int?,
@JsonProperty("quality") override var quality: SearchQuality? = null,
@JsonProperty("posterHeaders") override var posterHeaders: Map<String, String>? = null,
) : SearchResponse {
fun toLibraryItem(): SyncAPI.LibraryItem? {
return SyncAPI.LibraryItem(
name,
url,
id?.toString() ?: return null,
null,
null,
null,
latestUpdatedTime,
apiName, type, posterUrl, posterHeaders, quality, this.id
)
}
}
data class BookmarkedData( data class BookmarkedData(
@JsonProperty("id") override var id: Int?, @JsonProperty("id") override var id: Int?,
@JsonProperty("bookmarkedTime") val bookmarkedTime: Long, @JsonProperty("bookmarkedTime") val bookmarkedTime: Long,
@ -63,7 +92,7 @@ object DataStoreHelper {
null, null,
null, null,
null, null,
null, latestUpdatedTime,
apiName, type, posterUrl, posterHeaders, quality, this.id apiName, type, posterUrl, posterHeaders, quality, this.id
) )
} }
@ -75,9 +104,7 @@ object DataStoreHelper {
@JsonProperty("apiName") override val apiName: String, @JsonProperty("apiName") override val apiName: String,
@JsonProperty("type") override var type: TvType? = null, @JsonProperty("type") override var type: TvType? = null,
@JsonProperty("posterUrl") override var posterUrl: String?, @JsonProperty("posterUrl") override var posterUrl: String?,
@JsonProperty("watchPos") val watchPos: PosDur?, @JsonProperty("watchPos") val watchPos: PosDur?,
@JsonProperty("id") override var id: Int?, @JsonProperty("id") override var id: Int?,
@JsonProperty("parentId") val parentId: Int?, @JsonProperty("parentId") val parentId: Int?,
@JsonProperty("episode") val episode: Int?, @JsonProperty("episode") val episode: Int?,
@ -204,6 +231,41 @@ object DataStoreHelper {
return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString()) return getKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString())
} }
fun getAllSubscriptions(): List<SubscribedData> {
return getKeys("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA")?.mapNotNull {
getKey(it)
} ?: emptyList()
}
fun removeSubscribedData(id: Int?) {
if (id == null) return
AccountManager.localListApi.requireLibraryRefresh = true
removeKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
}
/**
* Set new seen episodes and update time
**/
fun updateSubscribedData(id: Int?, data: SubscribedData?, episodeResponse: EpisodeResponse?) {
if (id == null || data == null || episodeResponse == null) return
val newData = data.copy(
latestUpdatedTime = unixTimeMS,
lastSeenEpisodeCount = episodeResponse.getLatestEpisodes()
)
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), newData)
}
fun setSubscribedData(id: Int?, data: SubscribedData) {
if (id == null) return
setKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString(), data)
AccountManager.localListApi.requireLibraryRefresh = true
}
fun getSubscribedData(id: Int?): SubscribedData? {
if (id == null) return null
return getKey("$currentAccount/$RESULT_SUBSCRIBED_STATE_DATA", id.toString())
}
fun setViewPos(id: Int?, pos: Long, dur: Long) { fun setViewPos(id: Int?, pos: Long, dur: Long) {
if (id == null) return if (id == null) return
if (dur < 30_000) return // too short if (dur < 30_000) return // too short

View file

@ -14,6 +14,7 @@ import androidx.core.app.NotificationCompat
import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.MainActivity
import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.utils.AppUtils.createNotificationChannel
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -47,24 +48,12 @@ class PackageInstallerService : Service() {
.setSmallIcon(R.drawable.rdload) .setSmallIcon(R.drawable.rdload)
} }
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel =
NotificationChannel(UPDATE_CHANNEL_ID, UPDATE_CHANNEL_NAME, importance).apply {
description = UPDATE_CHANNEL_DESCRIPTION
}
// Register the channel with the system
val notificationManager: NotificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
override fun onCreate() { override fun onCreate() {
createNotificationChannel() this.createNotificationChannel(
UPDATE_CHANNEL_ID,
UPDATE_CHANNEL_NAME,
UPDATE_CHANNEL_DESCRIPTION
)
startForeground(UPDATE_NOTIFICATION_ID, baseNotification.build()) startForeground(UPDATE_NOTIFICATION_ID, baseNotification.build())
} }

View file

@ -20,6 +20,7 @@ import androidx.work.Data
import androidx.work.ExistingWorkPolicy import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import com.bumptech.glide.load.model.GlideUrl
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull
@ -213,7 +214,7 @@ object VideoDownloadManager {
} }
private val cachedBitmaps = hashMapOf<String, Bitmap>() private val cachedBitmaps = hashMapOf<String, Bitmap>()
private fun Context.getImageBitmapFromUrl(url: String): Bitmap? { fun Context.getImageBitmapFromUrl(url: String, headers: Map<String, String>? = null): Bitmap? {
try { try {
if (cachedBitmaps.containsKey(url)) { if (cachedBitmaps.containsKey(url)) {
return cachedBitmaps[url] return cachedBitmaps[url]
@ -221,12 +222,14 @@ object VideoDownloadManager {
val bitmap = GlideApp.with(this) val bitmap = GlideApp.with(this)
.asBitmap() .asBitmap()
.load(url).into(720, 720) .load(GlideUrl(url) { headers ?: emptyMap() })
.into(720, 720)
.get() .get()
if (bitmap != null) { if (bitmap != null) {
cachedBitmaps[url] = bitmap cachedBitmaps[url] = bitmap
} }
return null return bitmap
} catch (e: Exception) { } catch (e: Exception) {
logError(e) logError(e)
return null return null

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="?attr/white"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zM18,16v-5c0,-3.07 -1.63,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.64,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2zM16,17L8,17v-6c0,-2.48 1.51,-4.5 4,-4.5s4,2.02 4,4.5v6z"/>
</vector>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:width="108dp"
android:height="108dp"
android:viewportWidth="50"
android:viewportHeight="50"
android:name="vector">
<group android:scaleX="0.1755477"
android:scaleY="0.1755477"
android:translateX="0"
android:translateY="0">
<path android:name="path"
android:pathData="M 245.05 148.63 C 242.249 148.627 239.463 149.052 236.79 149.89 C 235.151 141.364 230.698 133.63 224.147 127.931 C 217.597 122.233 209.321 118.893 200.65 118.45 C 195.913 105.431 186.788 94.458 174.851 87.427 C 162.914 80.396 148.893 77.735 135.21 79.905 C 121.527 82.074 109.017 88.941 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.615 148.64 23.582 151.553 18.403 156.733 C 13.223 161.912 10.31 168.945 10.31 176.27 C 10.31 183.595 13.223 190.628 18.403 195.807 C 23.582 200.987 30.615 203.9 37.94 203.9 L 245.05 203.9 C 252.375 203.9 259.408 200.987 264.587 195.807 C 269.767 190.628 272.68 183.595 272.68 176.27 C 272.68 168.945 269.767 161.912 264.587 156.733 C 259.408 151.553 252.375 148.64 245.05 148.64 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
tools:ignore="VectorPath"
android:fillAlpha="0.55"/>
<path android:name="path_1" android:pathData="M 208.61 125 C 208.61 123.22 208.55 121.45 208.48 119.69 C 205.919 119.01 203.296 118.595 200.65 118.45 C 195.913 105.431 186.788 94.458 174.851 87.427 C 162.914 80.396 148.893 77.735 135.21 79.905 C 121.527 82.074 109.017 88.941 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.615 148.64 23.582 151.553 18.403 156.733 C 13.223 161.912 10.31 168.945 10.31 176.27 C 10.31 183.595 13.223 190.628 18.403 195.807 C 23.582 200.987 30.615 203.9 37.94 203.9 L 179 203.9 C 198.116 182.073 208.646 154.015 208.61 125 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
android:fillAlpha="0.55"/>
<path android:name="path_2" android:pathData="M 99.84 99.32 C 89.871 95.945 79.051 96.024 69.133 99.545 C 59.215 103.065 50.765 109.826 45.155 118.73 C 39.545 127.634 37.094 138.174 38.2 148.64 L 37.94 148.64 C 30.783 148.665 23.909 151.471 18.779 156.461 C 13.648 161.452 10.653 168.246 10.43 175.399 C 10.207 182.553 12.773 189.52 17.583 194.82 C 22.392 200.121 29.079 203.349 36.22 203.82 C 67.216 202.93 96.673 189.98 118.284 167.742 C 139.895 145.504 151.997 115.689 152 84.68 C 152 83 151.94 81.33 151.87 79.68 C 149.443 79.361 146.998 79.194 144.55 79.18 C 136.095 79.171 127.735 80.962 120.026 84.434 C 112.317 87.907 105.435 92.982 99.84 99.32 Z"
android:fillColor="#FFFFFF" android:strokeWidth="1"
android:fillAlpha="1"/>
</group>
</vector>

View file

@ -57,6 +57,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="50dp" android:layout_height="50dp"
android:id="@+id/media_route_button_holder" android:id="@+id/media_route_button_holder"
android:animateLayoutChanges="true"
android:layout_gravity="center_vertical|end"> android:layout_gravity="center_vertical|end">
<androidx.mediarouter.app.MediaRouteButton <androidx.mediarouter.app.MediaRouteButton
@ -69,15 +70,35 @@
app:mediaRouteButtonTint="?attr/textColor" /> app:mediaRouteButtonTint="?attr/textColor" />
<ImageView <ImageView
android:visibility="gone"
android:nextFocusUp="@id/result_back" android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_description" android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_add_sync" android:nextFocusLeft="@id/result_add_sync"
android:nextFocusRight="@id/result_share"
tools:visibility="visible"
android:id="@+id/result_subscribe"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_margin="5dp"
android:elevation="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/baseline_notifications_none_24"
android:layout_gravity="end|center_vertical"
app:tint="?attr/textColor" />
<ImageView
android:nextFocusUp="@id/result_back"
android:nextFocusDown="@id/result_description"
android:nextFocusLeft="@id/result_subscribe"
android:nextFocusRight="@id/result_open_in_browser" android:nextFocusRight="@id/result_open_in_browser"
android:id="@+id/result_share" android:id="@+id/result_share"
android:layout_width="25dp" android:layout_width="25dp"
android:layout_height="25dp" android:layout_height="25dp"
android:layout_marginEnd="10dp" android:layout_margin="5dp"
android:elevation="10dp" android:elevation="10dp"
android:background="?android:attr/selectableItemBackgroundBorderless" android:background="?android:attr/selectableItemBackgroundBorderless"

View file

@ -266,8 +266,8 @@
<string name="video_buffer_length_settings">طول التخزين المؤقت</string> <string name="video_buffer_length_settings">طول التخزين المؤقت</string>
<string name="video_buffer_disk_settings">التخزين المؤقت للفيديو على القرص</string> <string name="video_buffer_disk_settings">التخزين المؤقت للفيديو على القرص</string>
<string name="video_buffer_clear_settings">مسح التخزين المؤقت للصورة والفيديو</string> <string name="video_buffer_clear_settings">مسح التخزين المؤقت للصورة والفيديو</string>
<string name="video_ram_description">يتسبب في حدوث أعطال إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات الذاكرة المنخفضة ، مثل Android TV.</string> <string name="video_ram_description">يتسبب في حدوث أعطال إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات الذاكرة المنخفضة، مثل تلفزيون أندرويد.</string>
<string name="video_disk_description">يسبب مشاكل إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات مساحة التخزين المنخفضة ، مثل Android TV.</string> <string name="video_disk_description">يسبب مشاكل إذا تم ضبطه على مستوى مرتفع جدا على الأجهزة ذات مساحة التخزين المنخفضة، مثل تلفزيون أندرويد.</string>
<string name="dns_pref">إستخدام DNS بدلا من HTTPS</string> <string name="dns_pref">إستخدام DNS بدلا من HTTPS</string>
<string name="dns_pref_summary">مفيد لتجاوز حجب مزود خدمة الإنترنت</string> <string name="dns_pref_summary">مفيد لتجاوز حجب مزود خدمة الإنترنت</string>
<string name="add_site_pref">موقع بديل (نسخة)</string> <string name="add_site_pref">موقع بديل (نسخة)</string>
@ -360,7 +360,7 @@
https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog https://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog
--> -->
<string name="subtitles_example_text">نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق</string> <string name="subtitles_example_text">نصٌّ حكيمٌ لهُ سِرٌّ قاطِعٌ وَذُو شَأنٍ عَظيمٍ مكتوبٌ على ثوبٍ أخضرَ ومُغلفٌ بجلدٍ أزرق</string>
<string name="recommended">مُوصي به</string> <string name="recommended">مُوصى به</string>
<string name="player_loaded_subtitles" formatted="true">تم تحميل %s</string> <string name="player_loaded_subtitles" formatted="true">تم تحميل %s</string>
<string name="player_load_subtitles">إختيار ملف</string> <string name="player_load_subtitles">إختيار ملف</string>
<string name="player_load_subtitles_online">تحميل من الانترنت</string> <string name="player_load_subtitles_online">تحميل من الانترنت</string>
@ -543,4 +543,16 @@
<string name="pref_category_android_tv">تلفزيون أندرويد</string> <string name="pref_category_android_tv">تلفزيون أندرويد</string>
<string name="android_tv_interface_on_seek_settings_summary">مدة التقديم عنما يكون المشغل مرئيا</string> <string name="android_tv_interface_on_seek_settings_summary">مدة التقديم عنما يكون المشغل مرئيا</string>
<string name="android_tv_interface_on_seek_settings">مدة التقديم- المشغل المرئي</string> <string name="android_tv_interface_on_seek_settings">مدة التقديم- المشغل المرئي</string>
<string name="test_failed">فشل</string>
<string name="test_passed">نجح</string>
<string name="category_provider_test">إختبار المزود</string>
<string name="restart">إعادة التشغيل</string>
<string name="test_log">سجل</string>
<string name="start">بَدأ</string>
<string name="stop">إيقاف</string>
<string name="subscription_in_progress_notification">تحديث العروض التي تم الاشتراك فيها</string>
<string name="subscription_deleted">إلغاء الاشتراك من %s</string>
<string name="subscription_episode_released">تم إصدار الحلقة %d!</string>
<string name="subscription_list_name">مشترك</string>
<string name="subscription_new">مشترك في %s</string>
</resources> </resources>

View file

@ -535,4 +535,16 @@
<string name="android_tv_interface_on_seek_settings">Zobrazený přehrávač - doba hledání</string> <string name="android_tv_interface_on_seek_settings">Zobrazený přehrávač - doba hledání</string>
<string name="pref_category_android_tv">Android TV</string> <string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings_summary">Množství vyhledávané doby při zobrazeném přehrávači</string> <string name="android_tv_interface_on_seek_settings_summary">Množství vyhledávané doby při zobrazeném přehrávači</string>
<string name="test_log">Protokol</string>
<string name="category_provider_test">Test poskytovatele</string>
<string name="test_failed">Neúspěšné</string>
<string name="test_passed">Úspěšné</string>
<string name="restart">Restart</string>
<string name="start">Spustit</string>
<string name="stop">Zastavit</string>
<string name="subscription_in_progress_notification">Aktualizace odebíraných pořadů</string>
<string name="subscription_new">Přihlášeno k odběru %s</string>
<string name="subscription_deleted">Odhlášen odběr od %s</string>
<string name="subscription_episode_released">Byla vydána epizoda %d!</string>
<string name="subscription_list_name">Odebíráno</string>
</resources> </resources>

View file

@ -53,7 +53,7 @@
<string name="type_dropped">Abgebrochen</string> <string name="type_dropped">Abgebrochen</string>
<string name="type_plan_to_watch">Geplant</string> <string name="type_plan_to_watch">Geplant</string>
<string name="type_none">Nichts</string> <string name="type_none">Nichts</string>
<string name="type_re_watching">Erneut anschauen</string> <string name="type_re_watching">Erneut schauen</string>
<string name="play_movie_button">Film abspielen</string> <string name="play_movie_button">Film abspielen</string>
<string name="play_livestream_button">Livestream abspielen</string> <string name="play_livestream_button">Livestream abspielen</string>
<string name="play_torrent_button">Torrent streamen</string> <string name="play_torrent_button">Torrent streamen</string>
@ -212,7 +212,7 @@
<string name="no_subtitles">Keine Untertitel</string> <string name="no_subtitles">Keine Untertitel</string>
<string name="default_subtitles">Standard</string> <string name="default_subtitles">Standard</string>
<string name="free_storage">Frei</string> <string name="free_storage">Frei</string>
<string name="used_storage">Benutzt</string> <string name="used_storage">Belegt</string>
<string name="app_storage">App</string> <string name="app_storage">App</string>
<string name="movies">Filme</string> <string name="movies">Filme</string>
<string name="tv_series">TV-Serien</string> <string name="tv_series">TV-Serien</string>
@ -284,7 +284,7 @@
<string name="resize_fill">Strecken</string> <string name="resize_fill">Strecken</string>
<string name="resize_zoom">Vergrößern</string> <string name="resize_zoom">Vergrößern</string>
<string name="legal_notice">Haftungsausschluss</string> <string name="legal_notice">Haftungsausschluss</string>
<string name="category_general">General</string> <string name="category_general">Allgemein</string>
<string name="random_button_settings">Zufalls-Button</string> <string name="random_button_settings">Zufalls-Button</string>
<string name="random_button_settings_desc">Zufallsbutton auf der Startseite anzeigen</string> <string name="random_button_settings_desc">Zufallsbutton auf der Startseite anzeigen</string>
<string name="provider_lang_settings">Anbieter-Sprachen</string> <string name="provider_lang_settings">Anbieter-Sprachen</string>
@ -460,11 +460,11 @@
<string name="automatic_plugin_download_summary">Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories.</string> <string name="automatic_plugin_download_summary">Automatische Installation aller noch nicht installierten Plugins aus hinzugefügten Repositories.</string>
<string name="redo_setup_process">Einrichtungsvorgang wiederholen</string> <string name="redo_setup_process">Einrichtungsvorgang wiederholen</string>
<string name="apk_installer_settings">APK-Installer</string> <string name="apk_installer_settings">APK-Installer</string>
<string name="apk_installer_settings_des">Einige Telefone unterstützen das neue Installationsprogramm für Pakete nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen.</string> <string name="apk_installer_settings_des">Einige Telefone unterstützen den neuen Package-Installer nicht. Benutze die Legacy-Option, wenn sich die Updates nicht installieren lassen.</string>
<string name="season_format">%s %d%s</string> <string name="season_format">%s %d%s</string>
<string name="pref_category_links">Links</string> <string name="pref_category_links">Links</string>
<string name="pref_category_app_updates">App-Updates</string> <string name="pref_category_app_updates">App-Updates</string>
<string name="pref_category_backup">Back-Up</string> <string name="pref_category_backup">Sicherung</string>
<string name="pref_category_extensions">Erweiterungen</string> <string name="pref_category_extensions">Erweiterungen</string>
<string name="pref_category_actions">Wartung</string> <string name="pref_category_actions">Wartung</string>
<string name="pref_category_cache">Cache</string> <string name="pref_category_cache">Cache</string>
@ -506,4 +506,16 @@
<string name="empty_library_logged_in_message">Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln.</string> <string name="empty_library_logged_in_message">Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln.</string>
<string name="safe_mode_file">Datei für abgesicherten Modus gefunden! <string name="safe_mode_file">Datei für abgesicherten Modus gefunden!
\nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird.</string> \nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird.</string>
<string name="android_tv_interface_off_seek_settings">Player ausgeblendet - Betrag zum vor- und zurückspulen</string>
<string name="android_tv_interface_on_seek_settings_summary">Der Betrag, welcher verwendet wird, wenn der Player eingeblendet ist</string>
<string name="android_tv_interface_off_seek_settings_summary">Der Betrag, welcher verwendet wird, wenn der Player ausgeblendet ist</string>
<string name="pref_category_android_tv">Android-TV</string>
<string name="android_tv_interface_on_seek_settings">Player eingeblendet - Betrag zum vor- und zurückspulen</string>
<string name="test_failed">Fehlgeschlagen</string>
<string name="test_passed">Erfolgreich</string>
<string name="category_provider_test">Anbieter-Test</string>
<string name="stop">Stopp</string>
<string name="test_log">Log</string>
<string name="start">Start</string>
<string name="restart">Neustarten</string>
</resources> </resources>

View file

@ -150,7 +150,7 @@
<string name="episodes">Επεισόδια</string> <string name="episodes">Επεισόδια</string>
<string name="episodes_range">%d-%d</string> <string name="episodes_range">%d-%d</string>
<string name="episode_format" formatted="true">%d %s</string> <string name="episode_format" formatted="true">%d %s</string>
<string name="season_short">Κ</string> <string name="season_short">Σ</string>
<string name="episode_short">E</string> <string name="episode_short">E</string>
<string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string> <string name="no_episodes_found">Δεν βρέθηκαν επεισόδια</string>
<string name="delete_file">Διαγραφή αρχείου</string> <string name="delete_file">Διαγραφή αρχείου</string>

View file

@ -511,4 +511,16 @@
<string name="pref_category_android_tv">Android TV</string> <string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings_summary">La cantidad de búsqueda utilizada cuando la jugadora es visible</string> <string name="android_tv_interface_on_seek_settings_summary">La cantidad de búsqueda utilizada cuando la jugadora es visible</string>
<string name="android_tv_interface_off_seek_settings_summary">La cantidad de búsqueda utilizada cuando el jugador está oculto</string> <string name="android_tv_interface_off_seek_settings_summary">La cantidad de búsqueda utilizada cuando el jugador está oculto</string>
<string name="stop">Parar</string>
<string name="test_failed">Falló</string>
<string name="test_log">Registro</string>
<string name="start">Empezar</string>
<string name="test_passed">Aprobado</string>
<string name="category_provider_test">Prueba del proveedor</string>
<string name="restart">Reiniciar</string>
<string name="subscription_list_name">Suscrito</string>
<string name="subscription_new">Suscrito a %s</string>
<string name="subscription_deleted">Darse de baja de %s</string>
<string name="subscription_in_progress_notification">Actualizando los programas suscritos</string>
<string name="subscription_episode_released">¡Episodio %d publicado!</string>
</resources> </resources>

View file

@ -6,12 +6,12 @@
<string name="title_downloads">Téléchargements</string> <string name="title_downloads">Téléchargements</string>
<string name="title_settings">Paramètres</string> <string name="title_settings">Paramètres</string>
<string name="search_hint">Rechercher…</string> <string name="search_hint">Rechercher…</string>
<string name="search_poster_img_des">Miniature</string> <string name="search_poster_img_des">Affiche</string>
<string name="no_data">Aucune Donnée</string> <string name="no_data">Aucune Donnée</string>
<string name="episode_more_options_des">Plus d\'options</string> <string name="episode_more_options_des">Plus d\'options</string>
<string name="go_back_img_des">Retour</string> <string name="go_back_img_des">Retour</string>
<string name="next_episode">Épisode suivant</string> <string name="next_episode">Épisode suivant</string>
<string name="result_poster_img_des">Miniature</string> <string name="result_poster_img_des">Affiche</string>
<string name="result_tags">Genres</string> <string name="result_tags">Genres</string>
<string name="result_share">Partager</string> <string name="result_share">Partager</string>
<string name="result_open_in_browser">Ouvrir dans le navigateur</string> <string name="result_open_in_browser">Ouvrir dans le navigateur</string>
@ -29,7 +29,7 @@
<string name="pick_subtitle">Sous-titres</string> <string name="pick_subtitle">Sous-titres</string>
<string name="reload_error">Réessayer la connection…</string> <string name="reload_error">Réessayer la connection…</string>
<string name="go_back">Retour</string> <string name="go_back">Retour</string>
<string name="episode_poster_img_des">Miniature de l\'Épisode</string> <string name="episode_poster_img_des">Affiche de l\'épisode</string>
<string name="play_episode">Lire l\'Épisode</string> <string name="play_episode">Lire l\'Épisode</string>
<!--<string name="need_storage">Permet de télécharger les épisodes</string>--> <!--<string name="need_storage">Permet de télécharger les épisodes</string>-->
<string name="download">Télécharger</string> <string name="download">Télécharger</string>
@ -51,10 +51,10 @@
<string name="pref_disable_acra">Désactiver le rapport de bug automatique</string> <string name="pref_disable_acra">Désactiver le rapport de bug automatique</string>
<string name="home_more_info">Plus d\'informations</string> <string name="home_more_info">Plus d\'informations</string>
<string name="home_expanded_hide">Cacher</string> <string name="home_expanded_hide">Cacher</string>
<string name="home_main_poster_img_des">Poster principal</string> <string name="home_main_poster_img_des">Affiche principale</string>
<string name="home_play">Lecture</string> <string name="home_play">Lecture</string>
<string name="home_info">Info</string> <string name="home_info">Infos</string>
<string name="home_next_random_img_des">Suivant Aléatoire</string> <string name="home_next_random_img_des">Aléatoire suivant</string>
<string name="home_change_provider_img_des">Changer le fournisseur</string> <string name="home_change_provider_img_des">Changer le fournisseur</string>
<string name="filter_bookmarks">Filtrer les marques-pages</string> <string name="filter_bookmarks">Filtrer les marques-pages</string>
<string name="error_bookmarks_text">Marque-pages</string> <string name="error_bookmarks_text">Marque-pages</string>
@ -211,7 +211,7 @@
<string name="actor_background">Arrière plan</string> <string name="actor_background">Arrière plan</string>
<string name="home_source">Source</string> <string name="home_source">Source</string>
<string name="home_random">Aléatoire</string> <string name="home_random">Aléatoire</string>
<string name="coming_soon">À venir </string> <string name="coming_soon">Bientôt disponible</string>
<string name="poster_image">Image de l\'affiche</string> <string name="poster_image">Image de l\'affiche</string>
<string name="authenticated_user">%s Connecté</string> <string name="authenticated_user">%s Connecté</string>
<string name="action_add_to_bookmarks">Définir le statut de visionage</string> <string name="action_add_to_bookmarks">Définir le statut de visionage</string>
@ -490,4 +490,22 @@
<string name="delayed_update_notice">L\'application sera mise à jour dès la fin de la session</string> <string name="delayed_update_notice">L\'application sera mise à jour dès la fin de la session</string>
<string name="plugin_downloaded">Plugin Téléchargé</string> <string name="plugin_downloaded">Plugin Téléchargé</string>
<string name="action_remove_from_watched">Retirer de la vue</string> <string name="action_remove_from_watched">Retirer de la vue</string>
<string name="library">Bibliothèque</string>
<string name="browser">Navigateur</string>
<string name="sort">Trier</string>
<string name="sort_rating_asc">Note (basse à haute)</string>
<string name="sort_rating_desc">Note (haut à bas)</string>
<string name="sort_alphabetical_a">Alphabétique (A à Z)</string>
<string name="empty_library_no_accounts_message">On dirait que votre bibliothèque est vide :(
\nConnectez-vous à un compte ou ajoutez des séries à votre bibliothèque locale</string>
<string name="empty_library_logged_in_message">Il semble que cette liste soit vide, essayez d\'en choisir une autre</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="sort_by">Trié par</string>
<string name="sort_alphabetical_z">Alphabétique (Z à A)</string>
<string name="select_library">Sélectionnez la bibliothèque</string>
<string name="open_with">Ouvrir avec</string>
<string name="sort_updated_new">Mis à jour (Nouveau vers ancien)</string>
<string name="sort_updated_old">Mis à jour (ancien vers nouveau)</string>
<string name="safe_mode_file">Fichier du mode sans échec trouvé !
\nAucune extension ne sera chargée au démarrage avant que le fichier ne soit enlevé.</string>
</resources> </resources>

View file

@ -531,4 +531,21 @@
<string name="empty_library_logged_in_message">Čini se da je ova lista prazna, pokušajte se prebaciti na drugu</string> <string name="empty_library_logged_in_message">Čini se da je ova lista prazna, pokušajte se prebaciti na drugu</string>
<string name="safe_mode_file">Pronađena datoteka sigurnog načina rada! <string name="safe_mode_file">Pronađena datoteka sigurnog načina rada!
\nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni.</string> \nNe učitavaju se ekstenzije pri pokretanju dok se datoteka ne ukloni.</string>
<string name="android_tv_interface_on_seek_settings">Prikazan player- iznos preskakanja</string>
<string name="android_tv_interface_on_seek_settings_summary">Količina preskakanja koja se koristi kada je player vidljiv</string>
<string name="android_tv_interface_off_seek_settings">Player skriven - Količina preskakanja</string>
<string name="android_tv_interface_off_seek_settings_summary">Količina preskakanja koja se koristi kada je player skriven</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="test_passed">Prošlo</string>
<string name="restart">Restart</string>
<string name="test_log">Log</string>
<string name="start">Početak</string>
<string name="test_failed">Neuspješno</string>
<string name="stop">Stop</string>
<string name="category_provider_test">Test pružatelja usluga</string>
<string name="subscription_in_progress_notification">Ažuriram pretplaćene serije</string>
<string name="subscription_episode_released">Epizoda %d izbačena!</string>
<string name="subscription_list_name">Pretplaćeno</string>
<string name="subscription_new">Pretplaćen na %s</string>
<string name="subscription_deleted">Otkazana pretplata sa %s</string>
</resources> </resources>

View file

@ -35,7 +35,7 @@
<string name="skip_loading">Skip Loading</string> <string name="skip_loading">Skip Loading</string>
<string name="loading">Loading…</string> <string name="loading">Loading…</string>
<string name="type_watching">Sedang Menonton</string> <string name="type_watching">Sedang Menonton</string>
<string name="type_on_hold">Tertahan</string> <string name="type_on_hold">Tertunda</string>
<string name="type_completed">Selesai</string> <string name="type_completed">Selesai</string>
<string name="type_dropped">Dihentikan</string> <string name="type_dropped">Dihentikan</string>
<string name="type_plan_to_watch">Rencana untuk Menonton</string> <string name="type_plan_to_watch">Rencana untuk Menonton</string>
@ -387,7 +387,7 @@
<string name="episode_format" formatted="true">%d %s</string> <string name="episode_format" formatted="true">%d %s</string>
<string name="nsfw">17+</string> <string name="nsfw">17+</string>
<string name="others">Lainnya</string> <string name="others">Lainnya</string>
<string name="other_singular">Vidio</string> <string name="other_singular">Video</string>
<string name="add_site_pref">Duplikasi Website</string> <string name="add_site_pref">Duplikasi Website</string>
<string name="add_site_summary">Duplikasi website yang telah ada, dengan alamat berbeda</string> <string name="add_site_summary">Duplikasi website yang telah ada, dengan alamat berbeda</string>
<string name="pref_category_links">Tautan</string> <string name="pref_category_links">Tautan</string>
@ -395,7 +395,7 @@
<string name="pref_category_backup">Cadangkan</string> <string name="pref_category_backup">Cadangkan</string>
<string name="pref_category_extensions">Fitur Tambahan</string> <string name="pref_category_extensions">Fitur Tambahan</string>
<string name="play_with_app_name">Putar di CloudStream</string> <string name="play_with_app_name">Putar di CloudStream</string>
<string name="pref_filter_search_quality">Sembunyikan kualitas vidio terpilih di pencarian</string> <string name="pref_filter_search_quality">Sembunyikan kualitas video terpilih di pencarian</string>
<string name="season_format">%s %d%s</string> <string name="season_format">%s %d%s</string>
<string name="livestreams">Siaran langsung</string> <string name="livestreams">Siaran langsung</string>
<string name="remove_site_pref">Hapus Website</string> <string name="remove_site_pref">Hapus Website</string>
@ -444,7 +444,7 @@
<string name="extension_rating" formatted="true">Peringkat: %s</string> <string name="extension_rating" formatted="true">Peringkat: %s</string>
<string name="extension_authors">Pembuat</string> <string name="extension_authors">Pembuat</string>
<string name="extension_language">Bahasa</string> <string name="extension_language">Bahasa</string>
<string name="player_pref">Pemutar vidio utama</string> <string name="player_pref">Pemutar video utama</string>
<string name="player_settings_play_in_app">Pemutar Bawaan</string> <string name="player_settings_play_in_app">Pemutar Bawaan</string>
<string name="player_settings_play_in_vlc">VLC</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_mpv">MPV</string>
@ -475,7 +475,7 @@
<string name="subtitles_remove_captions">Hapus teks tertutup dari subtitel</string> <string name="subtitles_remove_captions">Hapus teks tertutup dari subtitel</string>
<string name="subtitles_remove_bloat">Hapus karakter sampah dari subtitel</string> <string name="subtitles_remove_bloat">Hapus karakter sampah dari subtitel</string>
<string name="audio_tracks">Audio Trek</string> <string name="audio_tracks">Audio Trek</string>
<string name="video_tracks">Vidio Trek</string> <string name="video_tracks">Video Trek</string>
<string name="extension_types">Dukungan</string> <string name="extension_types">Dukungan</string>
<string name="hls_playlist">Daftar putar HLS</string> <string name="hls_playlist">Daftar putar HLS</string>
<string name="apk_installer_settings">Penginstal APK</string> <string name="apk_installer_settings">Penginstal APK</string>
@ -529,4 +529,21 @@
<string name="empty_library_logged_in_message">Yahh daftar ini kosong, coba ganti ke yang lain</string> <string name="empty_library_logged_in_message">Yahh daftar ini kosong, coba ganti ke yang lain</string>
<string name="safe_mode_file">Mode aman file ditemukan! <string name="safe_mode_file">Mode aman file ditemukan!
\nTidak memuat ekstensi pada startup sampai berkas dihapus.</string> \nTidak memuat ekstensi pada startup sampai berkas dihapus.</string>
<string name="android_tv_interface_off_seek_settings">Sembunyikan Pemutaran - Geser</string>
<string name="android_tv_interface_on_seek_settings">Pemutar terlihat - Geser</string>
<string name="android_tv_interface_on_seek_settings_summary">Geser untuk menghilangkan</string>
<string name="android_tv_interface_off_seek_settings_summary">Geser untuk menghilangkan</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="test_log">Log</string>
<string name="test_passed">Berhasil</string>
<string name="category_provider_test">Tes provider</string>
<string name="stop">Berhenti</string>
<string name="start">Mulai</string>
<string name="restart">Mulai lagi</string>
<string name="test_failed">Gagal</string>
<string name="subscription_in_progress_notification">Memperbarui acara langganan</string>
<string name="subscription_list_name">Berlangganan</string>
<string name="subscription_new">Berlangganan ke %s</string>
<string name="subscription_deleted">Berhenti berlangganan di %s</string>
<string name="subscription_episode_released">Episode %d telah rilis!</string>
</resources> </resources>

View file

@ -528,4 +528,16 @@
<string name="empty_library_logged_in_message">Sembra che questa lista sia vuota, prova a passare a un\'altra</string> <string name="empty_library_logged_in_message">Sembra che questa lista sia vuota, prova a passare a un\'altra</string>
<string name="safe_mode_file">File \"safe mode\" trovato! <string name="safe_mode_file">File \"safe mode\" trovato!
\nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso.</string> \nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso.</string>
<string name="android_tv_interface_off_seek_settings_summary">Quantità di ricerca usata quando il player è nascosto</string>
<string name="pref_category_android_tv">TV Android</string>
<string name="android_tv_interface_on_seek_settings_summary">Quantità di ricerca usata quando il player è visibile</string>
<string name="android_tv_interface_on_seek_settings">Player visibile - Quantità di ricerca</string>
<string name="android_tv_interface_off_seek_settings">Player nascosto - Quantità di ricerca</string>
<string name="test_log">Registro</string>
<string name="start">Avvia</string>
<string name="category_provider_test">Test del provider</string>
<string name="restart">Riavvia</string>
<string name="stop">Ferma</string>
<string name="test_passed">Superato</string>
<string name="test_failed">Fallito</string>
</resources> </resources>

View file

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="next_episode_time_min_format" formatted="true">%d分</string>
<string name="title_downloads">ダウンロード</string>
<string name="title_search">検索</string>
<string name="title_settings">設定</string>
<string name="result_share">シェア</string>
<string name="movies">映画</string>
<string name="title_home">ホーム</string>
<string name="library">ライブラリ</string>
<string name="home_play">再生</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="search_hint">検索…</string>
<string name="download">ダウンロード</string>
<string name="home_info">情報</string>
<string name="season">シーズン</string>
<string name="trailer">予告編</string>
<string name="tv_series_singular">シリーズ</string>
<string name="episodes">エピソード</string>
<string name="player_speed_text_format" formatted="true">再生速度 (%.2fx)</string>
<string name="next_episode">次のエピソード</string>
<string name="sort_apply">適用</string>
<string name="category_account">アカウント</string>
<string name="cartoons">カートゥーン</string>
<string name="tv_series">TVシリーズ</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="movies_singular">映画</string>
<string name="others">その他</string>
<string name="cartoons_singular">カートゥーン</string>
<string name="torrent_singular">トレント</string>
<string name="documentaries_singular">ドキュメンタリー</string>
<string name="asian_drama_singular">アジアドラマ</string>
<string name="live_singular">ライブ配信</string>
<string name="nsfw_singular">NSFW</string>
<string name="sort_cancel">キャンセル</string>
<string name="anime">アニメ</string>
<string name="video_lock">ロック</string>
<string name="video_source">ソース</string>
<string name="nsfw">NSFW</string>
<string name="clear_history">履歴を削除</string>
<string name="continue_watching">視聴中コンテンツ</string>
<string name="category_general">全般</string>
<string name="other_singular">動画</string>
<string name="category_player">プレーヤー</string>
<string name="type_plan_to_watch">懐う</string>
<string name="play_trailer_button">予告編を再生</string>
<string name="episode_short">エピソード</string>
<string name="type_watching">視聴</string>
<string name="result_tags">ジャンル</string>
<string name="play_movie_button">映画を再生</string>
<string name="pick_subtitle">字幕</string>
<string name="app_name">CloudStream</string>
<string name="play_with_app_name">CloudStreamで再生</string>
<string name="browser">ブラウザ</string>
<string name="type_completed">完成</string>
<string name="type_dropped">放置</string>
<string name="type_on_hold">保留</string>
<string name="loading">ローディング…</string>
<string name="result_open_in_browser">ブラウザで開く</string>
<string name="season_short">シーズン</string>
<string name="resume_time_left" formatted="true">残り
\n%d分</string>
<string name="play_episode">再生エピソード</string>
<string name="downloaded">ダウンロード済</string>
<string name="pref_category_backup">バックアップ</string>
<string name="home_source">ソース</string>
<string name="history">履歴</string>
<string name="result_poster_img_des">ポスター</string>
<string name="type_none">なし</string>
<string name="sort_copy">コピー</string>
<string name="sort_close">閉じる</string>
<string name="sort_save">保存</string>
<string name="sort_clear">消去</string>
<string name="app_dub_sub_episode_text_format" formatted="true">%sエピ%d</string>
<string name="cast_format" formatted="true">出演者:%s</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="rated_format" formatted="true">視聴率 %.1f</string>
<string name="new_update_format" formatted="true">新しいアップデートを発見!
\n%s -&gt; %s</string>
<string name="duration_format" formatted="true">%d分</string>
<string name="search_hint_site" formatted="true">%sを検索…</string>
<string name="pick_source">ソース</string>
<string name="filler" formatted="true">ろくごうきじ</string>
<string name="reload_error">接続を再試行…</string>
<string name="go_back">戻り</string>
<string name="action_remove_from_bookmarks">削除</string>
<string name="home_more_info">詳細情報</string>
<string name="home_expanded_hide">閉じる</string>
<string name="category_updates">アップデート・バックアップ</string>
<string name="app_language">アプリ言語</string>
<string name="github">GitHubギットハブ</string>
<string name="go_back_30">-30</string>
<string name="go_forward_30">+30</string>
<string name="legal_notice">免責</string>
<string name="pref_category_extensions">拡張機能</string>
<string name="pref_category_app_updates">アプリ更新</string>
<string name="category_providers">提供者</string>
<string name="pref_category_subtitles">字幕</string>
<string name="pref_category_ui_features">特徴</string>
<string name="pref_category_defaults">デフォルト</string>
<string name="automatic">自動</string>
<string name="home_random">任意</string>
<string name="extensions">拡張機能</string>
<string name="pref_category_links">リンク</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="login">ログイン</string>
<string name="logout">ログアウト</string>
<string name="max">最大</string>
<string name="min">最小</string>
<string name="none">なし</string>
<string name="next"></string>
<string name="is_adult">18+</string>
<string name="no"></string>
<string name="open_with">で開く</string>
<string name="episode">エピソード</string>
<string name="duration">時間</string>
<string name="synopsis">概要</string>
<string name="site">サイト</string>
<string name="used_storage">使用</string>
<string name="app_storage">アプリ</string>
<string name="action_open_watching">詳細情報</string>
<string name="action_remove_watching">削除</string>
<string name="picture_in_picture">ピクチャーインピクチャー</string>
<string name="player_subtitles_settings">字幕</string>
<string name="settings_info">情報</string>
<string name="pause">一時停止</string>
<string name="play_episode_toast">再生エピソード</string>
<string name="delete">削除</string>
<string name="start">開始</string>
<string name="status">状態</string>
<string name="year"></string>
<string name="resume">再開</string>
<string name="test_failed">失敗</string>
<string name="test_passed">合格</string>
<string name="free_storage">空き</string>
<string name="status_completed">完成</string>
<string name="status_ongoing">進行中</string>
<string name="normal">デフォルト</string>
<string name="player_settings_play_in_browser">ウェブブラウザ</string>
<string name="player_settings_play_in_vlc">VLC</string>
<string name="player_settings_play_in_mpv">MPV</string>
<string name="extension_language">言語</string>
<string name="extension_authors">作成者</string>
<string name="extension_size">サイズ</string>
<string name="extension_status">状態</string>
<string name="extension_version">バージョン</string>
<string name="extension_rating" formatted="true">視聴率 %s</string>
<string name="rating">視聴率</string>
<string name="default_subtitles">デフォルト</string>
<string name="download_failed">ダウンロード失敗</string>
<string name="download_started">ダウンロード開始</string>
<string name="download_done">ダウンロード完了</string>
<string name="download_canceled">ダウンロード終了</string>
<string name="stream">ストリーム</string>
<string name="update_started">アップデート開始</string>
<string name="no_season">シーズンなし</string>
<string name="no_subtitles">字幕なし</string>
<string name="video_aspect_ratio_resize">アスペクト比</string>
<string name="skip_loading">ロードをスキップする</string>
<string name="episode_more_options_des">その他のオプション</string>
<string name="no_data">データなし</string>
<string name="downloading">ダウンロード中</string>
<string name="error_bookmarks_text">ブックマーク</string>
<string name="download_storage_text">内部記憶装置</string>
<string name="download_paused">ダウンロードが一時停止</string>
<string name="provider_info_meta">メタデータはこのサイトでは提供されません。メタデータがサイト上に存在しない場合、ビデオの読み込みに失敗します。</string>
<string name="torrent_plot">記述</string>
<string name="show_log_cat">Logcat 🐈を表示</string>
<string name="test_log">ログ</string>
<string name="search">検索</string>
<string name="discord">Discordに参加</string>
<string name="update">アップデート</string>
<string name="check_for_update">アップデートを確認</string>
<string name="show_title">作品名</string>
<string name="update_notification_installing">アプリのアップデートをインストール中…</string>
</resources>

View file

@ -1,3 +1,128 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_dub_sub_episode_text_format" formatted="true">%sಎಪಿ%d</string>
<string name="cast_format" formatted="true">ಕ್ಯಾಸ್ಟ್:%s</string>
<string name="go_back_img_des">ಹಿಂದೆ ಹೋಗು</string>
<string name="filler" formatted="true">ಫಿಲ್ಲರ್</string>
<string name="title_search">ಹುಡುಕು</string>
<string name="title_downloads">ಡೌನ್ಲೋಡ್</string>
<string name="subs_font">ಫಾಂಟ್</string>
<string name="search_provider_text_providers">ಪೂರೈಕೆದಾರರನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ</string>
<string name="search_provider_text_types">ಪ್ರಕಾರಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಹುಡುಕಿ</string>
<string name="benene_count_text_none">ಯಾವುದೇ ಬೆನೆನ್ಸ್ ನೀಡಿಲ್ಲ</string>
<string name="subs_auto_select_language">ಸ್ವಯಂ-ಆಯ್ಕೆ ಭಾಷೆ</string>
<string name="action_open_watching">ಹೆಚ್ಚಿನ ಮಾಹಿತಿ</string>
<string name="action_open_play">\@ಸ್ಟ್ರಿಂಗ್/ಹೋಮ್_ಪ್ಲೇ</string>
<string name="vpn_might_be_needed">ಈ ಪೂರೈಕೆದಾರರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಲು VPN ಬೇಕಾಗಬಹುದು</string>
<string name="player_size_settings_des">ಕಪ್ಪು ಗಡಿಗಳನ್ನು ತೆಗೆದುಹಾಕಿ</string>
<string name="next_episode_format" formatted="true">ಸಂಚಿಕೆ%d ಬಿಡುಗಡೆಯಾಗಲಿದೆ</string>
<string name="next_episode_time_hour_format" formatted="true">%dh %dm</string>
<string name="result_poster_img_des">ಪೋಸ್ಟರ್</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="update_started">ಅಪ್ಡೇಟ್ ಪ್ರಾರಂಭವಾಗಿದೆ</string>
<string name="error_loading_links_toast">ಲೋಡಿಂಗ್ ಲಿಂಕ್ ಎರರ್ ಬಂದಿದೆ</string>
<string name="download_storage_text">ಇಂಟರ್ನಲ್ ಸ್ಟೋರೇಜ್</string>
<string name="app_dubbed_text">ಡಬ್</string>
<string name="app_subbed_text">ಸಬ್</string>
<string name="pref_disable_acra">ಸ್ವಯಂಚಾಲಿತ ದೋಷ ವರದಿಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ</string>
<string name="home_expanded_hide">ಹೈಡ್</string>
<string name="home_play">ಪ್ಲೇ</string>
<string name="home_info">ಮಾಹಿತಿ</string>
<string name="action_add_to_bookmarks">ಸೆಟ್ ವಾಚ್ ಸ್ಟೇಟಸ್</string>
<string name="sort_apply">ಅನ್ವಯಿಸು</string>
<string name="sort_cancel">ರದ್ದುಮಾಡು</string>
<string name="subs_subtitle_elevation">ಸಬ್ ಟೈಟಲ್ಸ್ ಎಲೆವಷನ್</string>
<string name="subs_font_size">ಫಾಂಟ್ ಸೈಜ್</string>
<string name="subs_subtitle_languages">ಸಬ್ ಟೈಟಲ್ಸ್ ಭಾಷೆ</string>
<string name="action_remove_watching">ತೆಗೆದುಹಾಕಿ</string>
<string name="vpn_torrent">ಈ ಪೂರೈಕೆದಾರರು ಟೊರೆಂಟ್ ಆಗಿದೆ, VPN ಅನ್ನು ಶಿಫಾರಸು ಮಾಡಲಾಗಿದೆ</string>
<string name="normal_no_plot">ಯಾವುದೇ ಪ್ಲಾಟ್ ಕಂಡುಬಂದಿಲ್ಲ</string>
<string name="show_log_cat">ಲಾಗ್‌ಕ್ಯಾಟ್ 🐈 ತೋರಿಸಿ</string>
<string name="test_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="chromecast_subtitles_settings_des">ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="go_back">ಹಿಂದೆ ಹೋಗು</string>
<string name="popup_pause_download">ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಿ</string>
<string name="error_bookmarks_text">ಬುಕ್‌ಮಾರ್ಕ್‌</string>
<string name="subs_background_color">ಬ್ಯಾಕ್ ಗ್ರೌಂಡ್ ಕಲರ್</string>
<string name="benene_count_text">%d ಡೇವ್‌ಗಳಿಗೆ ಬೆನೆನೆಸ್ ನೀಡಲಾಗಿದೆ</string>
<string name="subs_hold_to_reset_to_default">ಡೀಫಾಲ್ಟ್‌ಗೆ ಮರುಹೊಂದಿಸಲು ಹಿಡಿದುಕೊಳ್ಳಿ</string>
<string name="provider_info_meta">ಸೈಟ್‌ನಿಂದ ಮೆಟಾಡೇಟಾವನ್ನು ಒದಗಿಸಲಾಗಿಲ್ಲ, ಅದು ಸೈಟ್‌ನಲ್ಲಿ ಅಸ್ತಿತ್ವದಲ್ಲಿಲ್ಲದಿದ್ದರೆ ವೀಡಿಯೊ ಲೋಡಿಂಗ್ ವಿಫಲಗೊಳ್ಳುತ್ತದೆ.</string>
<string name="picture_in_picture_des">ಇತರ ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಮೇಲೆ ಚಿಕಣಿ ಪ್ಲೇಯರ್‌ನಲ್ಲಿ ಪ್ಲೇಬ್ಯಾಕ್ ಅನ್ನು ಮುಂದುವರಿಸುತ್ತದೆ</string>
<string name="chromecast_subtitles_settings">ಕ್ರೋಮ್ ಕ್ಯಾಸ್ಟ್ ಸಬ್ ಟೈಟಲ್ಸ್</string>
<string name="rated_format" formatted="true">ರೇಟೆಡ್:%.1f</string>
<string name="action_remove_from_bookmarks">ತೆಗೆದುಹಾಕಿ</string>
<string name="popup_resume_download">ಡೌನ್‌ಲೋಡ್ ಅನ್ನು ಪುನರಾರಂಭಿಸಿ</string>
<string name="sort_close">ಕ್ಲೋಸ್</string>
<string name="sort_clear">ಕ್ಲಿಯರ್</string>
<string name="sort_save">ಸೇವ್</string>
<string name="subtitles_settings">ಸಬ್ ಟೈಟಲ್ಸ್ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="popup_play_file">ಫೈಲ್ ಪ್ಲೇ</string>
<string name="subs_text_color">ಟೆಕ್ಸ್ಟ್ ಕಲರ್</string>
<string name="subs_outline_color">ಔಟ್ ಲೈನ್ ಕಲರ್</string>
<string name="subs_window_color">ವಿಂಡೋ ಕಲರ್</string>
<string name="subs_edge_type">ಎಡ್ಜ್ ಟೈಪ್</string>
<string name="home_change_provider_img_des">ಪ್ರೊವೈಡರ್ ಬದಲಾಯಿಸಿ</string>
<string name="duration_format" formatted="true">%dಮಿನ</string>
<string name="torrent_plot">ವಿವರಣೆ</string>
<string name="player_speed_text_format" formatted="true">ಸ್ಪೀಡ್(%.2fx)</string>
<string name="title_home">ಹೋಂ</string>
<string name="pick_subtitle">ಸಬ್ ಟೈಟಲ್ಸ್</string>
<string name="title_settings">ಸೆಟ್ಟಿಂಗ್ಸ್</string>
<string name="filter_bookmarks">ಬುಕ್‌ಮಾರ್ಕ್‌ಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ</string>
<string name="search_hint">ಹುಡುಕು…</string>
<string name="play_movie_button">ಚಲನಚಿತ್ರವನ್ನು ಪ್ಲೇ ಮಾಡಿ</string>
<string name="preview_background_img_des">ಪ್ರಿವ್ಯೂ ಹಿನ್ನೆಲೆ</string>
<string name="next_episode">ಮುಂದಿನ ಸಂಚಿಕೆ</string>
<string name="app_name">ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್</string>
<string name="downloading">ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ</string>
<string name="stream">ಸ್ಟ್ರೀಮ್</string>
<string name="result_share">ಶೇರ್</string>
<string name="popup_delete_file">ಫೈಲ್ ಅಳಿಸಿ</string>
<string name="home_more_info">ಹೆಚ್ಚಿನ ಮಾಹಿತಿ</string>
<string name="new_update_format" formatted="true">ಹೊಸ ಅಪ್ಡೇಟ್ ಬಂದಿದೆ
\n%s-%s</string>
<string name="loading">ಲೋಡಿಂಗ್…</string>
<string name="subs_download_languages">ಡೌನ್‌ಲೋಡ್ ಭಾಷೆಗಳನ್ನು ಮಾಡಿ</string>
<string name="play_livestream_button">ಲೈವ್‌ಸ್ಟ್ರೀಮ್ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="play_with_app_name">ಕ್ಲೌಡ್ ಸ್ಟ್ರೀಮ್ ಇದರೊಂದಿಗೆ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="type_plan_to_watch">ವೀಕ್ಷಿಸಲು ಯೋಜನೆ</string>
<string name="play_episode">ಸಂಚಿಕೆಯನ್ನು ಪ್ಲೇ ಮಾಡಿ</string>
<string name="continue_watching">ಕಂಟಿನ್ಯೂ ವಾಟಚಿಂಗ್</string>
<string name="torrent_no_plot">ಯಾವುದೇ ವಿವರಣೆ ಕಂಡುಬಂದಿಲ್ಲ</string>
<string name="play_torrent_button">ಸ್ಟ್ರೀಮ್ ಟೊರೆಂಟ್</string>
<string name="download">ಡೌನ್‌ಲೋಡ್</string>
<string name="sort_copy">ಕಾಪಿ</string>
<string name="no_data">ನೋ ಡೇಟಾ</string>
<string name="player_speed">ಪ್ಲೇಯರ್ ಸ್ಪೀಡ್</string>
<string name="next_episode_time_day_format" formatted="true">%d %dh %dm</string>
<string name="search_hint_site" formatted="true">ಹುಡುಕು %s…</string>
<string name="episode_more_options_des">ಹೆಚ್ಚಿನ ಆಯ್ಕೆ</string>
<string name="subs_import_text" formatted="true">ಫಾಂಟ್‌ಗಳನ್ನು ಇರಿಸುವ ಮೂಲಕ ಆಮದು ಮಾಡಿ %s</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string>
<string name="result_tags">ಪ್ರಕಾರಗಳು</string>
<string name="result_open_in_browser">ಬ್ರೌಸರ್ ತೆರೆಯಿರಿ</string>
<string name="type_on_hold">ಆನ್-ಹೋಲ್ಡ್</string>
<string name="type_none">ನನ್</string>
<string name="reload_error">ಸಂಪರ್ಕವನ್ನು ಮರುಪ್ರಯತ್ನಿಸಿ…</string>
<string name="download_paused">ಡೌನ್‌ಲೋಡ್ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ</string>
<string name="download_failed">ಡೌನ್‌ಲೋಡ್ ವಿಫಲವಾಗಿದೆ</string>
<string name="download_done">ಡೌನ್‌ಲೋಡ್ ಮುಗಿದಿದೆ</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_re_watching">ಪುನಃ ವೀಕ್ಷಿಸುತ್ತಿದೆ</string>
<string name="play_trailer_button">ಟ್ರೈಲರ್ ಪ್ಲೇ ಮಾಡಿ</string>
<string name="pick_source">ಮೂಲಗಳು</string>
<string name="downloaded">ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗಿದೆ</string>
<string name="download_started">ಡೌನ್‌ಲೋಡ್ ಪ್ರಾರಂಭವಾಗಿದೆ</string>
<string name="download_canceled">ಡೌನ್‌ಲೋಡ್ ರದ್ದುಗೊಳಿಸಲಾಗಿದೆ</string>
<string name="home_next_random_img_des">ಮುಂದಿನ ರಾಂಡಮ್</string>
</resources> </resources>

View file

@ -373,7 +373,7 @@
<string name="skip_setup">Pomiń setup</string> <string name="skip_setup">Pomiń setup</string>
<string name="app_layout_subtext">Dostosuj wygląd aplikacji do urządzenia</string> <string name="app_layout_subtext">Dostosuj wygląd aplikacji do urządzenia</string>
<string name="crash_reporting_title">Zgłaszanie błędów</string> <string name="crash_reporting_title">Zgłaszanie błędów</string>
<string name="preferred_media_subtext">Co chciałbyś obejrzeć\?</string> <string name="preferred_media_subtext">Co chciałbyś obejrzeć</string>
<string name="setup_done">Gotowe</string> <string name="setup_done">Gotowe</string>
<string name="extensions">Rozszerzenia</string> <string name="extensions">Rozszerzenia</string>
<string name="add_repository">Dodaj repozytorium</string> <string name="add_repository">Dodaj repozytorium</string>
@ -509,4 +509,9 @@
<string name="empty_library_logged_in_message">Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną</string> <string name="empty_library_logged_in_message">Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną</string>
<string name="safe_mode_file">Znaleziono plik trybu bezpiecznego. <string name="safe_mode_file">Znaleziono plik trybu bezpiecznego.
\nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty.</string> \nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty.</string>
<string name="android_tv_interface_on_seek_settings_summary">Używana ilość przewijania, gdy widoczny jest odtwarzacz</string>
<string name="android_tv_interface_off_seek_settings">Ukryty odtwarzacz - ilość przewijania</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_on_seek_settings">Pokazany odtwarzacz — ilość przewijania</string>
<string name="android_tv_interface_off_seek_settings_summary">Używana ilość przewijania, gdy ukryty jest odtwarzacz</string>
</resources> </resources>

View file

@ -9,7 +9,7 @@
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- 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="result_poster_img_des">Poster</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">Poster</string>
<string name="episode_poster_img_des">Poster Episod</string> <string name="episode_poster_img_des">Poster Episod</string>
<string name="home_main_poster_img_des">Poster Principal</string> <string name="home_main_poster_img_des">Poster Principal</string>
<string name="home_next_random_img_des">Următorul la Întâmplare</string> <string name="home_next_random_img_des">Următorul la Întâmplare</string>
@ -142,7 +142,7 @@
<string name="restore_success">Fișier de rezervă încărcat</string> <string name="restore_success">Fișier de rezervă încărcat</string>
<string name="restore_failed_format" formatted="true">Imposibilitatea de a restaura datele din %s</string> <string name="restore_failed_format" formatted="true">Imposibilitatea de a restaura datele din %s</string>
<string name="backup_success">Date stocate</string> <string name="backup_success">Date stocate</string>
<string name="backup_failed">Permisiuni de arhivare lipsă, vă rugăm să încercați din nou</string> <string name="backup_failed">Permisiunea de arhivare lipșe, vă rugăm să încercați din nou.</string>
<string name="backup_failed_error_format">Eroare de backup %s</string> <string name="backup_failed_error_format">Eroare de backup %s</string>
<string name="search">Căutare</string> <string name="search">Căutare</string>
<string name="category_account">Conturi și credite</string> <string name="category_account">Conturi și credite</string>
@ -154,7 +154,7 @@
<string name="bug_report_settings_on">Nu trimiteți niciun fel de date</string> <string name="bug_report_settings_on">Nu trimiteți niciun fel de date</string>
<string name="show_fillers_settings">Afișează etichetele [filler] pentru anime</string> <string name="show_fillers_settings">Afișează etichetele [filler] pentru anime</string>
<string name="show_trailers_settings">Arată trailerul</string> <string name="show_trailers_settings">Arată trailerul</string>
<string name="kitsu_settings">Arată posterele de la Kitsu</string> <string name="kitsu_settings">Arată afișele de la Kitsu</string>
<string name="updates_settings">Afișați actualizările aplicației</string> <string name="updates_settings">Afișați actualizările aplicației</string>
<string name="updates_settings_des">Căutați automat noi actualizări la pornire</string> <string name="updates_settings_des">Căutați automat noi actualizări la pornire</string>
<string name="uprereleases_settings">Actualizați la prerelease</string> <string name="uprereleases_settings">Actualizați la prerelease</string>
@ -384,4 +384,8 @@
<string name="autoplay_next_settings_des">Începe următorul episod când se termină episodul curent</string> <string name="autoplay_next_settings_des">Începe următorul episod când se termină episodul curent</string>
<string name="pref_filter_search_quality">Ascundeți calitatea video selectată în rezultatele căutării</string> <string name="pref_filter_search_quality">Ascundeți calitatea video selectată în rezultatele căutării</string>
<string name="play_livestream_button">Redare Livestream</string> <string name="play_livestream_button">Redare Livestream</string>
<string name="library">Librărie</string>
<string name="test_log">Log</string>
<string name="browser">Browser</string>
<string name="play_with_app_name">Joacă cu CloudStream</string>
</resources> </resources>

View file

@ -506,4 +506,16 @@
<string name="android_tv_interface_on_seek_settings">Плеер показан - Перемотки объем</string> <string name="android_tv_interface_on_seek_settings">Плеер показан - Перемотки объем</string>
<string name="android_tv_interface_off_seek_settings">Плеер спрятан - Перемотки объем</string> <string name="android_tv_interface_off_seek_settings">Плеер спрятан - Перемотки объем</string>
<string name="subtitles_remove_bloat">Удалять лишнее из субтитров</string> <string name="subtitles_remove_bloat">Удалять лишнее из субтитров</string>
<string name="android_tv_interface_off_seek_settings_summary">Местоположение ползунка, когда игрок скрыт</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="actor_supporting">Второго планa</string>
<string name="skip_type_mixed_op">Смешанный опенинг</string>
<string name="skip_type_mixed_ed">Смешанный конец</string>
<string name="category_provider_test">Тест провайдер</string>
<string name="test_log">Журнал</string>
<string name="start">Запустить</string>
<string name="test_passed">Выполнено</string>
<string name="test_failed">Неудачный</string>
<string name="stop">Прекратить</string>
<string name="restart">Перезапустить</string>
</resources> </resources>

View file

@ -17,7 +17,7 @@
<string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string> <string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string>
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<string name="duration_format" formatted="true">%d min</string> <string name="duration_format" formatted="true">%d min</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">Plagát</string>
<string name="episode_poster_img_des">Plagát epizódy</string> <string name="episode_poster_img_des">Plagát epizódy</string>
<string name="home_main_poster_img_des">Hlavný plagát</string> <string name="home_main_poster_img_des">Hlavný plagát</string>
<string name="play_with_app_name">Prehrať s CloudStream</string> <string name="play_with_app_name">Prehrať s CloudStream</string>

View file

@ -507,4 +507,20 @@
<string name="safe_mode_file">Файл безпечного режиму знайдено! <string name="safe_mode_file">Файл безпечного режиму знайдено!
\nРозширеня не завантажуються під час запуску, доки файл не буде видалено.</string> \nРозширеня не завантажуються під час запуску, доки файл не буде видалено.</string>
<string name="pref_category_android_tv">Android TV</string> <string name="pref_category_android_tv">Android TV</string>
<string name="android_tv_interface_off_seek_settings">Плеєр сховано - обсяг пошуку</string>
<string name="android_tv_interface_on_seek_settings">Плеєр показано - обсяг пошуку</string>
<string name="android_tv_interface_on_seek_settings_summary">Обсяг пошуку, який використовується, коли плеєр видимий</string>
<string name="android_tv_interface_off_seek_settings_summary">Обсяг пошуку, який використовується, коли гравець прихований</string>
<string name="test_failed">Не вдалося</string>
<string name="test_passed">Пройдено</string>
<string name="restart">Перезапуск</string>
<string name="test_log">Журнал</string>
<string name="start">Старт</string>
<string name="stop">Стоп</string>
<string name="category_provider_test">Тест постачальника</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="subscription_episode_released">Епізод %d випущено!</string>
</resources> </resources>

View file

@ -19,7 +19,7 @@
<string name="next_episode_time_min_format" formatted="true">%dm</string> <string name="next_episode_time_min_format" formatted="true">%dm</string>
<!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS -->
<string name="result_poster_img_des">封面</string> <string name="result_poster_img_des">封面</string>
<string name="search_poster_img_des">\@string/result_poster_img_des</string> <string name="search_poster_img_des">封面</string>
<string name="episode_poster_img_des">劇集封面</string> <string name="episode_poster_img_des">劇集封面</string>
<string name="home_main_poster_img_des">主封面</string> <string name="home_main_poster_img_des">主封面</string>
<string name="home_next_random_img_des">隨機下一個</string> <string name="home_next_random_img_des">隨機下一個</string>
@ -533,4 +533,5 @@
<string name="pref_category_defaults">預設</string> <string name="pref_category_defaults">預設</string>
<string name="pref_category_looks">外觀</string> <string name="pref_category_looks">外觀</string>
<string name="pref_category_ui_features">功能</string> <string name="pref_category_ui_features">功能</string>
<string name="browser">瀏覽器</string>
</resources> </resources>

View file

@ -554,4 +554,21 @@
<string name="empty_library_no_accounts_message">看来您的库是空的 :( <string name="empty_library_no_accounts_message">看来您的库是空的 :(
\n登录库账户或添加节目到您的本地库</string> \n登录库账户或添加节目到您的本地库</string>
<string name="empty_library_logged_in_message">看来此列表是空的,请尝试切换到另一个</string> <string name="empty_library_logged_in_message">看来此列表是空的,请尝试切换到另一个</string>
<string name="android_tv_interface_on_seek_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="pref_category_android_tv">Android TV</string>
<string name="test_failed">失败</string>
<string name="category_provider_test">片源测试</string>
<string name="restart">重启</string>
<string name="stop">停止</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="start">开始</string>
<string name="subscription_episode_released">第 %d 集已发布!</string>
<string name="test_passed">成功</string>
<string name="test_log">日志</string>
</resources> </resources>

View file

@ -44,6 +44,7 @@
<string name="random_button_key" translatable="false">random_button_key</string> <string name="random_button_key" translatable="false">random_button_key</string>
<string name="provider_lang_key" translatable="false">provider_lang_key</string> <string name="provider_lang_key" translatable="false">provider_lang_key</string>
<string name="dns_key" translatable="false">dns_key</string> <string name="dns_key" translatable="false">dns_key</string>
<string name="jsdelivr_proxy_key" translatable="false">jsdelivr_proxy_key</string>
<string name="download_path_key" translatable="false">download_path_key</string> <string name="download_path_key" translatable="false">download_path_key</string>
<string name="app_name_download_path" translatable="false">Cloudstream</string> <string name="app_name_download_path" translatable="false">Cloudstream</string>
<string name="app_layout_key" translatable="false">app_layout_key</string> <string name="app_layout_key" translatable="false">app_layout_key</string>
@ -380,6 +381,9 @@
<string name="video_disk_description">Causes problems if set too high on devices with low storage space, such as Android TV.</string> <string name="video_disk_description">Causes problems if set too high on devices with low storage space, such as Android TV.</string>
<string name="dns_pref">DNS over HTTPS</string> <string name="dns_pref">DNS over HTTPS</string>
<string name="dns_pref_summary">Useful for bypassing ISP blocks</string> <string name="dns_pref_summary">Useful for bypassing ISP blocks</string>
<string name="jsdelivr_proxy">raw.githubusercontent.com Proxy</string>
<string name="jsdelivr_enabled">Failed to reach GitHub, enabling jsdelivr proxy.</string>
<string name="jsdelivr_proxy_summary">Bypasses blocking of GitHub using jsdelivr, may cause updates to be delayed by few days.</string>
<string name="add_site_pref">Clone site</string> <string name="add_site_pref">Clone site</string>
<string name="remove_site_pref">Remove site</string> <string name="remove_site_pref">Remove site</string>
<string name="add_site_summary">Add a clone of an existing site, with a different URL</string> <string name="add_site_summary">Add a clone of an existing site, with a different URL</string>
@ -407,6 +411,7 @@
responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use
CloudStream 3 at your own risk. CloudStream 3 at your own risk.
</string> </string>
<string name="pref_category_bypass">ISP Bypasses</string>
<string name="pref_category_links">Links</string> <string name="pref_category_links">Links</string>
<string name="pref_category_app_updates">App updates</string> <string name="pref_category_app_updates">App updates</string>
<string name="pref_category_backup">Backup</string> <string name="pref_category_backup">Backup</string>
@ -646,4 +651,10 @@
<string name="empty_library_no_accounts_message">Looks like your library is empty :(\nLogin to a library account or add shows to your local library</string> <string name="empty_library_no_accounts_message">Looks like your library is empty :(\nLogin to a library account or add shows to your local library</string>
<string name="empty_library_logged_in_message">Looks like this list is empty, try switching to another one</string> <string name="empty_library_logged_in_message">Looks like this list is empty, try switching to another one</string>
<string name="safe_mode_file">Safe mode file found!\nNot loading any extensions on startup until file is removed.</string> <string name="safe_mode_file">Safe mode file found!\nNot loading any extensions on startup until file is removed.</string>
<string name="revert">Revert</string>
<string name="subscription_in_progress_notification">Updating subscribed shows</string>
<string name="subscription_list_name">Subscribed</string>
<string name="subscription_new">Subscribed to %s</string>
<string name="subscription_deleted">Unsubscribed from %s</string>
<string name="subscription_episode_released">Episode %d released!</string>
</resources> </resources>

View file

@ -6,18 +6,6 @@
android:title="@string/app_language" android:title="@string/app_language"
android:icon="@drawable/ic_baseline_language_24" /> android:icon="@drawable/ic_baseline_language_24" />
<Preference
android:key="@string/override_site_key"
android:title="@string/add_site_pref"
android:summary="@string/add_site_summary"
android:icon="@drawable/ic_baseline_add_24" />
<Preference
android:key="@string/dns_key"
android:title="@string/dns_pref"
android:summary="@string/dns_pref_summary"
android:icon="@drawable/ic_baseline_dns_24" />
<Preference <Preference
android:key="@string/download_path_key" android:key="@string/download_path_key"
android:title="@string/download_path_pref" android:title="@string/download_path_pref"
@ -34,6 +22,30 @@
android:icon="@drawable/benene" android:icon="@drawable/benene"
app:summary="@string/benene_des" /> app:summary="@string/benene_des" />
<PreferenceCategory
android:title="@string/pref_category_bypass">
<Preference
android:key="@string/override_site_key"
android:title="@string/add_site_pref"
android:summary="@string/add_site_summary"
android:icon="@drawable/ic_baseline_add_24" />
<Preference
android:key="@string/dns_key"
android:title="@string/dns_pref"
android:summary="@string/dns_pref_summary"
android:icon="@drawable/ic_baseline_dns_24" />
<SwitchPreference
android:defaultValue="false"
android:icon="@drawable/ic_github_logo"
android:key="@string/jsdelivr_proxy_key"
android:title="@string/jsdelivr_proxy"
android:summary="@string/jsdelivr_proxy_summary" />
</PreferenceCategory>
<PreferenceCategory <PreferenceCategory
android:title="@string/pref_category_links"> android:title="@string/pref_category_links">