Merge remote-tracking branch 'origin/master'

This commit is contained in:
Sofie99 2023-09-09 19:26:34 +07:00
commit 6ac802f29c
14 changed files with 578 additions and 281 deletions

View file

@ -250,9 +250,9 @@ dependencies {
// used for subtitle decoding https://github.com/albfernandez/juniversalchardet
implementation("com.github.albfernandez:juniversalchardet:2.4.0")
// newpipe yt taken from https://github.com/TeamNewPipe/NewPipe/blob/dev/app/build.gradle#L204
// newpipe yt taken from https://github.com/TeamNewPipe/NewPipeExtractor/commits/dev
// this should be updated frequently to avoid trailer fu*kery
implementation("com.github.TeamNewPipe:NewPipeExtractor:1f08d28")
implementation("com.github.teamnewpipe:NewPipeExtractor:1f08d28")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
// Library/extensions searching with Levenshtein distance

View file

@ -5,7 +5,9 @@ import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.annotation.JsonProperty
import com.lagradost.cloudstream3.AcraApplication
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
@ -13,6 +15,7 @@ import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mapper
import com.lagradost.cloudstream3.mvvm.debugAssert
import com.lagradost.cloudstream3.mvvm.debugPrint
import com.lagradost.cloudstream3.mvvm.logError
@ -33,6 +36,9 @@ import java.text.SimpleDateFormat
import java.time.Instant
import java.util.Date
import java.util.TimeZone
import kotlin.time.Duration
import kotlin.time.DurationUnit
import kotlin.time.toDuration
class SimklApi(index: Int) : AccountManager(index), SyncAPI {
override var name = "Simkl"
@ -59,6 +65,80 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
*/
private var lastScoreTime = -1L
private object SimklCache {
private const val SIMKL_CACHE_KEY = "SIMKL_API_CACHE"
enum class CacheTimes(val value: String) {
OneMonth("30d"),
ThirtyMinutes("30m")
}
private class SimklCacheWrapper<T>(
@JsonProperty("obj") val obj: T?,
@JsonProperty("validUntil") val validUntil: Long,
@JsonProperty("cacheTime") val cacheTime: Long = unixTime,
) {
/** Returns true if cache is newer than cacheDays */
fun isFresh(): Boolean {
return validUntil > unixTime
}
fun remainingTime(): Duration {
val unixTime = unixTime
return if (validUntil > unixTime) {
(validUntil - unixTime).toDuration(DurationUnit.SECONDS)
} else {
Duration.ZERO
}
}
}
fun cleanOldCache() {
getKeys(SIMKL_CACHE_KEY)?.forEach {
val isOld = AcraApplication.getKey<SimklCacheWrapper<Any>>(it)?.isFresh() == false
if (isOld) {
removeKey(it)
}
}
}
fun <T> setKey(path: String, value: T, cacheTime: Duration) {
debugPrint { "Set cache: $SIMKL_CACHE_KEY/$path for ${cacheTime.inWholeDays} days or ${cacheTime.inWholeSeconds} seconds." }
setKey(
SIMKL_CACHE_KEY,
path,
// Storing as plain sting is required to make generics work.
SimklCacheWrapper(value, unixTime + cacheTime.inWholeSeconds).toJson()
)
}
/**
* Gets cached object, if object is not fresh returns null and removes it from cache
*/
inline fun <reified T : Any> getKey(path: String): T? {
// Required for generic otherwise "LinkedHashMap cannot be cast to MediaObject"
val type = mapper.typeFactory.constructParametricType(
SimklCacheWrapper::class.java,
T::class.java
)
val cache = getKey<String>(SIMKL_CACHE_KEY, path)?.let {
mapper.readValue<SimklCacheWrapper<T>>(it, type)
}
return if (cache?.isFresh() == true) {
debugPrint {
"Cache hit at: $SIMKL_CACHE_KEY/$path. " +
"Remains fresh for ${cache.remainingTime().inWholeDays} days or ${cache.remainingTime().inWholeSeconds} seconds."
}
cache.obj
} else {
debugPrint { "Cache miss at: $SIMKL_CACHE_KEY/$path" }
removeKey(SIMKL_CACHE_KEY, path)
null
}
}
}
companion object {
private const val clientId: String = BuildConfig.SIMKL_CLIENT_ID
private const val clientSecret: String = BuildConfig.SIMKL_CLIENT_SECRET
@ -210,18 +290,18 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("img") val img: String?
) {
companion object {
fun convertToEpisodes(list: List<EpisodeMetadata>?): List<MediaObject.Season.Episode> {
fun convertToEpisodes(list: List<EpisodeMetadata>?): List<MediaObject.Season.Episode>? {
return list?.map {
MediaObject.Season.Episode(it.episode)
} ?: emptyList()
}
}
fun convertToSeasons(list: List<EpisodeMetadata>?): List<MediaObject.Season> {
fun convertToSeasons(list: List<EpisodeMetadata>?): List<MediaObject.Season>? {
return list?.filter { it.season != null }?.groupBy {
it.season
}?.map { (season, episodes) ->
MediaObject.Season(season!!, convertToEpisodes(episodes))
} ?: emptyList()
}?.mapNotNull { (season, episodes) ->
convertToEpisodes(episodes)?.let { MediaObject.Season(season!!, it) }
}?.ifEmpty { null }
}
}
}
@ -235,11 +315,17 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("title") val title: String?,
@JsonProperty("year") val year: Int?,
@JsonProperty("ids") val ids: Ids?,
@JsonProperty("total_episodes") val total_episodes: Int? = null,
@JsonProperty("status") val status: String? = null,
@JsonProperty("poster") val poster: String? = null,
@JsonProperty("type") val type: String? = null,
@JsonProperty("seasons") val seasons: List<Season>? = null,
@JsonProperty("episodes") val episodes: List<Season.Episode>? = null
) {
fun hasEnded(): Boolean {
return status == "released" || status == "ended"
}
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class Season(
@JsonProperty("number") val number: Int,
@ -281,6 +367,194 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
}
class SimklScoreBuilder private constructor() {
data class Builder(
private var url: String? = null,
private var interceptor: Interceptor? = null,
private var ids: MediaObject.Ids? = null,
private var score: Int? = null,
private var status: Int? = null,
private var addEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
private var removeEpisodes: Pair<List<MediaObject.Season>?, List<MediaObject.Season.Episode>?>? = null,
) {
fun interceptor(interceptor: Interceptor) = apply { this.interceptor = interceptor }
fun apiUrl(url: String) = apply { this.url = url }
fun ids(ids: MediaObject.Ids) = apply { this.ids = ids }
fun score(score: Int?, oldScore: Int?) = apply {
if (score != oldScore) {
this.score = score
}
}
fun status(newStatus: Int?, oldStatus: Int?) = apply {
// Only set status if its new
if (newStatus != oldStatus) {
this.status = newStatus
} else {
this.status = null
}
}
fun episodes(
allEpisodes: List<EpisodeMetadata>?,
newEpisodes: Int?,
oldEpisodes: Int?,
) = apply {
if (allEpisodes == null || newEpisodes == null) return@apply
fun getEpisodes(rawEpisodes: List<EpisodeMetadata>) =
if (rawEpisodes.any { it.season != null }) {
EpisodeMetadata.convertToSeasons(rawEpisodes) to null
} else {
null to EpisodeMetadata.convertToEpisodes(rawEpisodes)
}
// Do not add episodes if there is no change
if (newEpisodes > (oldEpisodes ?: 0)) {
this.addEpisodes = getEpisodes(allEpisodes.take(newEpisodes))
}
if ((oldEpisodes ?: 0) > newEpisodes) {
this.removeEpisodes = getEpisodes(allEpisodes.drop(newEpisodes))
}
}
suspend fun execute(): Boolean {
val time = getDateTime(unixTime)
return if (this.status == SimklListStatusType.None.value) {
app.post(
"$url/sync/history/remove",
json = StatusRequest(
shows = listOf(HistoryMediaObject(ids = ids)),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} else {
val episodeRemovalResponse = removeEpisodes?.let { (seasons, episodes) ->
app.post(
"${this.url}/sync/history/remove",
json = StatusRequest(
shows = listOf(
HistoryMediaObject(
ids = ids,
seasons = seasons,
episodes = episodes
)
),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} ?: true
val historyResponse =
// Only post if there are episodes or score to upload
if (addEpisodes != null || score != null) {
app.post(
"${this.url}/sync/history",
json = StatusRequest(
shows = listOf(
HistoryMediaObject(
null,
null,
ids,
addEpisodes?.first,
addEpisodes?.second,
score,
score?.let { time },
)
), movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} else {
true
}
val statusResponse = status?.let { setStatus ->
val newStatus =
SimklListStatusType.values()
.firstOrNull { it.value == setStatus }?.originalName
?: SimklListStatusType.Watching.originalName!!
app.post(
"${this.url}/sync/add-to-list",
json = StatusRequest(
shows = listOf(
StatusMediaObject(
null,
null,
ids,
newStatus,
)
), movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} ?: true
statusResponse && episodeRemovalResponse && historyResponse
}
}
}
}
suspend fun getEpisodes(
simklId: Int?,
type: String?,
episodes: Int?,
hasEnded: Boolean?
): Array<EpisodeMetadata>? {
if (simklId == null) return null
val cacheKey = "Episodes/$simklId"
val cache = SimklCache.getKey<Array<EpisodeMetadata>>(cacheKey)
// Return cached result if its higher or equal the amount of episodes.
if (cache != null && cache.size >= (episodes ?: 0)) {
return cache
}
// There is always one season in Anime -> no request necessary
if (type == "anime" && episodes != null) {
return episodes.takeIf { it > 0 }?.let {
(1..it).map { episode ->
EpisodeMetadata(
null, null, null, episode, null
)
}.toTypedArray()
}
}
val url = when (type) {
"anime" -> "https://api.simkl.com/anime/episodes/$simklId"
"tv" -> "https://api.simkl.com/tv/episodes/$simklId"
"movie" -> return null
else -> return null
}
debugPrint { "Requesting episodes from $url" }
return app.get(url, params = mapOf("client_id" to clientId))
.parsedSafe<Array<EpisodeMetadata>>()?.also {
val cacheTime =
if (hasEnded == true) SimklCache.CacheTimes.OneMonth.value else SimklCache.CacheTimes.ThirtyMinutes.value
// 1 Month cache
SimklCache.setKey(cacheKey, it, Duration.parse(cacheTime))
}
}
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class HistoryMediaObject(
@JsonProperty("title") title: String? = null,
@JsonProperty("year") year: Int? = null,
@JsonProperty("ids") ids: Ids? = null,
@JsonProperty("seasons") seasons: List<Season>? = null,
@JsonProperty("episodes") episodes: List<Season.Episode>? = null,
@JsonProperty("rating") val rating: Int? = null,
@JsonProperty("rated_at") val rated_at: String? = null,
) : MediaObject(title, year, ids, seasons = seasons, episodes = episodes)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class RatingMediaObject(
@JsonProperty("title") title: String?,
@ -299,15 +573,6 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
@JsonProperty("watched_at") val watched_at: String? = getDateTime(unixTime)
) : MediaObject(title, year, ids)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class HistoryMediaObject(
@JsonProperty("title") title: String?,
@JsonProperty("year") year: Int?,
@JsonProperty("ids") ids: Ids?,
@JsonProperty("seasons") seasons: List<Season>?,
@JsonProperty("episodes") episodes: List<Season.Episode>?,
) : MediaObject(title, year, ids, seasons = seasons, episodes = episodes)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
data class StatusRequest(
@JsonProperty("movies") val movies: List<MediaObject>,
@ -404,13 +669,13 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
data class ShowMetadata(
override val last_watched_at: String?,
override val status: String,
override val user_rating: Int?,
override val last_watched: String?,
override val watched_episodes_count: Int?,
override val total_episodes_count: Int?,
val show: Show
@JsonProperty("last_watched_at") override val last_watched_at: String?,
@JsonProperty("status") override val status: String,
@JsonProperty("user_rating") override val user_rating: Int?,
@JsonProperty("last_watched") override val last_watched: String?,
@JsonProperty("watched_episodes_count") override val watched_episodes_count: Int?,
@JsonProperty("total_episodes_count") override val total_episodes_count: Int?,
@JsonProperty("show") val show: Show
) : Metadata {
override fun getIds(): Show.Ids {
return this.show.ids
@ -435,23 +700,23 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
data class Show(
val title: String,
val poster: String?,
val year: Int?,
val ids: Ids,
@JsonProperty("title") val title: String,
@JsonProperty("poster") val poster: String?,
@JsonProperty("year") val year: Int?,
@JsonProperty("ids") val ids: Ids,
) {
data class Ids(
val simkl: Int,
val slug: String?,
val imdb: String?,
val zap2it: String?,
val tmdb: String?,
val offen: String?,
val tvdb: String?,
val mal: String?,
val anidb: String?,
val anilist: String?,
val traktslug: String?
@JsonProperty("simkl") val simkl: Int,
@JsonProperty("slug") val slug: String?,
@JsonProperty("imdb") val imdb: String?,
@JsonProperty("zap2it") val zap2it: String?,
@JsonProperty("tmdb") val tmdb: String?,
@JsonProperty("offen") val offen: String?,
@JsonProperty("tvdb") val tvdb: String?,
@JsonProperty("mal") val mal: String?,
@JsonProperty("anidb") val anidb: String?,
@JsonProperty("anilist") val anilist: String?,
@JsonProperty("traktslug") val traktslug: String?
) {
fun matchesId(database: SyncServices, id: String): Boolean {
return when (database) {
@ -491,20 +756,58 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
}
/**
* Useful to get episodes on demand to prevent unnecessary requests.
*/
class SimklEpisodeConstructor(
private val simklId: Int?,
private val type: String?,
private val totalEpisodeCount: Int?,
private val hasEnded: Boolean?
) {
suspend fun getEpisodes(): Array<EpisodeMetadata>? {
return getEpisodes(simklId, type, totalEpisodeCount, hasEnded)
}
}
class SimklSyncStatus(
override var status: Int,
override var score: Int?,
val oldScore: Int?,
override var watchedEpisodes: Int?,
val episodes: Array<EpisodeMetadata>?,
val episodeConstructor: SimklEpisodeConstructor,
override var isFavorite: Boolean? = null,
override var maxEpisodes: Int? = null,
/** Save seen episodes separately to know the change from old to new.
* Required to remove seen episodes if count decreases */
val oldEpisodes: Int,
val oldStatus: String?
) : SyncAPI.AbstractSyncStatus()
override suspend fun getStatus(id: String): SyncAPI.AbstractSyncStatus? {
val realIds = readIdFromString(id)
// Key which assumes all ids are the same each time :/
// This could be some sort of reference system to make multiple IDs
// point to the same key.
val idKey =
realIds.toList().map { "${it.first.originalName}=${it.second}" }.sorted().joinToString()
val cachedObject = SimklCache.getKey<MediaObject>(idKey)
val searchResult: MediaObject = cachedObject
?: (searchByIds(realIds)?.firstOrNull()?.also { result ->
val cacheTime =
if (result.hasEnded()) SimklCache.CacheTimes.OneMonth.value else SimklCache.CacheTimes.ThirtyMinutes.value
SimklCache.setKey(idKey, result, Duration.parse(cacheTime))
}) ?: return null
val episodeConstructor = SimklEpisodeConstructor(
searchResult.ids?.simkl,
searchResult.type,
searchResult.total_episodes,
searchResult.hasEnded()
)
val foundItem = getSyncListSmart()?.let { list ->
listOf(list.shows, list.anime, list.movies).flatten().firstOrNull { show ->
realIds.any { (database, id) ->
@ -513,172 +816,63 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
}
}
// Search to get episodes
val searchResult = searchByIds(realIds)?.firstOrNull()
val episodes = getEpisodes(searchResult?.ids?.simkl, searchResult?.type)
if (foundItem != null) {
return SimklSyncStatus(
status = foundItem.status?.let { SimklListStatusType.fromString(it)?.value }
?: return null,
score = foundItem.user_rating,
watchedEpisodes = foundItem.watched_episodes_count,
maxEpisodes = foundItem.total_episodes_count,
episodes = episodes,
maxEpisodes = searchResult.total_episodes,
episodeConstructor = episodeConstructor,
oldEpisodes = foundItem.watched_episodes_count ?: 0,
oldScore = foundItem.user_rating,
oldStatus = foundItem.status
)
} else {
return if (searchResult != null) {
SimklSyncStatus(
status = SimklListStatusType.None.value,
score = 0,
watchedEpisodes = 0,
maxEpisodes = if (searchResult.type == "movie") 0 else null,
episodes = episodes,
oldEpisodes = 0,
)
} else {
null
}
return SimklSyncStatus(
status = SimklListStatusType.None.value,
score = 0,
watchedEpisodes = 0,
maxEpisodes = if (searchResult.type == "movie") 0 else searchResult.total_episodes,
episodeConstructor = episodeConstructor,
oldEpisodes = 0,
oldStatus = null,
oldScore = null
)
}
}
override suspend fun score(id: String, status: SyncAPI.AbstractSyncStatus): Boolean {
val parsedId = readIdFromString(id)
lastScoreTime = unixTime
if (status.status == SimklListStatusType.None.value) {
return app.post(
"$mainUrl/sync/history/remove",
json = StatusRequest(
shows = listOf(
HistoryMediaObject(
null,
null,
MediaObject.Ids.fromMap(parsedId),
emptyList(),
emptyList()
)
),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
}
val realScore = status.score
val ratingResponseSuccess = if (realScore != null) {
// Remove rating if score is 0
val ratingsSuffix = if (realScore == 0) "/remove" else ""
debugPrint { "Rate ${this.name} item: rating=$realScore" }
app.post(
"$mainUrl/sync/ratings$ratingsSuffix",
json = StatusRequest(
// Not possible to know if TV or Movie
shows = listOf(
RatingMediaObject(
null,
null,
MediaObject.Ids.fromMap(parsedId),
realScore
)
),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} else {
true
}
val simklStatus = status as? SimklSyncStatus
val builder = SimklScoreBuilder.Builder()
.apiUrl(this.mainUrl)
.score(status.score, simklStatus?.oldScore)
.status(status.status, (status as? SimklSyncStatus)?.oldStatus?.let { oldStatus ->
SimklListStatusType.values().firstOrNull {
it.originalName == oldStatus
}?.value
})
.interceptor(interceptor)
.ids(MediaObject.Ids.fromMap(parsedId))
// Get episodes only when required
val episodes = simklStatus?.episodeConstructor?.getEpisodes()
// All episodes if marked as completed
val watchedEpisodes = if (status.status == SimklListStatusType.Completed.value) {
simklStatus?.episodes?.size
episodes?.size
} else {
status.watchedEpisodes
}
// Only post episodes if available episodes and the status is correct
val episodeResponseSuccess =
if (simklStatus != null && watchedEpisodes != null && !simklStatus.episodes.isNullOrEmpty() && listOf(
SimklListStatusType.Paused.value,
SimklListStatusType.Dropped.value,
SimklListStatusType.Watching.value,
SimklListStatusType.Completed.value,
SimklListStatusType.ReWatching.value
).contains(status.status)
) {
suspend fun postEpisodes(
url: String,
rawEpisodes: List<EpisodeMetadata>
): Boolean {
val (seasons, episodes) = if (rawEpisodes.any { it.season != null }) {
EpisodeMetadata.convertToSeasons(rawEpisodes) to null
} else {
null to EpisodeMetadata.convertToEpisodes(rawEpisodes)
}
debugPrint { "Synced history using $url: seasons=${seasons?.toList()}, episodes=${episodes?.toList()}" }
return app.post(
url,
json = StatusRequest(
shows = listOf(
HistoryMediaObject(
null,
null,
MediaObject.Ids.fromMap(parsedId),
seasons,
episodes
)
),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
}
builder.episodes(episodes?.toList(), watchedEpisodes, simklStatus?.oldEpisodes)
// If episodes decrease: remove all episodes beyond watched episodes.
val removeResponse = if (simklStatus.oldEpisodes > watchedEpisodes) {
val removeEpisodes = simklStatus.episodes
.drop(watchedEpisodes)
postEpisodes("$mainUrl/sync/history/remove", removeEpisodes)
} else {
true
}
val cutEpisodes = simklStatus.episodes.take(watchedEpisodes)
val addResponse = postEpisodes("$mainUrl/sync/history/", cutEpisodes)
removeResponse && addResponse
} else true
val newStatus =
SimklListStatusType.values().firstOrNull { it.value == status.status }?.originalName
?: SimklListStatusType.Watching.originalName
val statusResponseSuccess = if (newStatus != null) {
debugPrint { "Add to ${this.name} list: status=$newStatus" }
app.post(
"$mainUrl/sync/add-to-list",
json = StatusRequest(
shows = listOf(
StatusMediaObject(
null,
null,
MediaObject.Ids.fromMap(parsedId),
newStatus
)
),
movies = emptyList()
),
interceptor = interceptor
).isSuccessful
} else {
true
}
debugPrint { "All scoring complete: rating=$ratingResponseSuccess, status=$statusResponseSuccess, episode=$episodeResponseSuccess" }
requireLibraryRefresh = true
return ratingResponseSuccess && statusResponseSuccess && episodeResponseSuccess
return builder.execute()
}
@ -694,17 +888,6 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
).parsedSafe()
}
suspend fun getEpisodes(simklId: Int?, type: String?): Array<EpisodeMetadata>? {
if (simklId == null) return null
val url = when (type) {
"anime" -> "https://api.simkl.com/anime/episodes/$simklId"
"tv" -> "https://api.simkl.com/tv/episodes/$simklId"
"movie" -> return null
else -> return null
}
return app.get(url, params = mapOf("client_id" to clientId)).parsedSafe()
}
override suspend fun search(name: String): List<SyncAPI.SyncSearchResult>? {
return app.get(
"$mainUrl/search/", params = mapOf("client_id" to clientId, "q" to name)
@ -737,16 +920,17 @@ class SimklApi(index: Int) : AccountManager(index), SyncAPI {
return null
}
private suspend fun getSyncListSince(since: Long?): AllItemsResponse {
private suspend fun getSyncListSince(since: Long?): AllItemsResponse? {
val params = getDateTime(since)?.let {
mapOf("date_from" to it)
} ?: emptyMap()
// Can return null on no change.
return app.get(
"$mainUrl/sync/all-items/",
params = params,
interceptor = interceptor
).parsed()
).parsedSafe()
}
private suspend fun getActivities(): ActivitiesResponse? {

View file

@ -200,4 +200,69 @@
<string name="use">استخدم</string>
<string name="unable_to_inflate">%sتعذر إنشاء واجهة المستخدم بشكل صحيح، وهذا خطأ كبير ويجب الإبلاغ عنه على الفور</string>
<string name="qualities">الصفات</string>
<string name="subs_edge_type">نوع الحافة</string>
<string name="home_play">العب</string>
<string name="error_loading_links_toast">حدث خطأ أثناء تحميل الروابط</string>
<string name="download_storage_text">التخزين الداخلي</string>
<string name="app_subbed_text">الترجمة</string>
<string name="popup_resume_download">استئناف تحميل</string>
<string name="home_info">معلومات</string>
<string name="popup_pause_download">وقفة التحميل</string>
<string name="sort_cancel">الغي</string>
<string name="sort_save">احفظ</string>
<string name="subtitles_settings">إعدادات الترجمة</string>
<string name="subs_text_color">لون الخط</string>
<string name="subs_outline_color">لون المخطط التفصيلي</string>
<string name="sort_close">اقفل</string>
<string name="sort_clear">امسح</string>
<string name="player_speed">سرعة اللاعب</string>
<string name="subs_background_color">لون الخلفية</string>
<string name="subs_window_color">لون النافذة</string>
<string name="subs_subtitle_elevation">ارتفاع الترجمة</string>
<string name="popup_delete_file">حذف ملف</string>
<string name="pref_disable_acra">تعطيل الإبلاغ التلقائي عن الأخطاء</string>
<string name="update_started">بدأ التحديث</string>
<string name="sort_copy">انسخ</string>
<string name="stream">بث</string>
<string name="popup_play_file">ملف اللعب</string>
<string name="home_more_info">مزيد من المعلومات</string>
<string name="filter_bookmarks">تصفية الإشارات المرجعية</string>
<string name="error_bookmarks_text">إشارات مرجعية</string>
<string name="action_remove_from_bookmarks">زيل</string>
<string name="action_add_to_bookmarks">ضبط حالة المشاهدة</string>
<string name="app_dubbed_text">مدبلجة</string>
<string name="home_expanded_hide">اخفي</string>
<string name="sort_apply">قدم</string>
<string name="torrent_plot">وصف</string>
<string name="picture_in_picture_des">يستمر التشغيل في مشغل مصغر فوق التطبيقات الأخرى</string>
<string name="delete_message" formatted="true">نهائيا %sسيؤدي هذا الى حذف
\nهل أنت متأكد؟</string>
<string name="subs_font">الخط</string>
<string name="subs_font_size">حجم الخط</string>
<string name="action_remove_watching">زيل</string>
<string name="vpn_torrent">هذا المزود عبارة عن تورنت، ويوصى باستخدام فيبيان</string>
<string name="provider_info_meta">لا يتم توفير البيانات الوصفية بواسطة الموقع، وسيفشل تحميل الفيديو إذا لم يكن موجودًا في الموقع.</string>
<string name="status_ongoing">جاري التنفيذ</string>
<string name="status_completed">مكتمل</string>
<string name="status">حالة</string>
<string name="subs_auto_select_language">التحديد التلقائي للغة</string>
<string name="player_size_settings">زر تغيير حجم المشغل</string>
<string name="continue_watching">مواصلة المشاهدة</string>
<string name="action_open_watching">مزيد من المعلومات</string>
<string name="search_provider_text_providers">البحث باستخدام مقدمي الخدمات</string>
<string name="search_provider_text_types">البحث باستخدام الأنواع</string>
<string name="benene_count_text">بنيني الى المطورين %d تم منح</string>
<string name="benene_count_text_none">لم يتم تقديم بنيني</string>
<string name="subs_download_languages">تحميل اللغات</string>
<string name="subs_subtitle_languages">لغة الترجمة</string>
<string name="subs_hold_to_reset_to_default">اضغط لإعادة التعيين إلى الوضع الافتراضي</string>
<string name="subs_import_text" formatted="true">%s قم باستيراد الخطوط بوضعها في</string>
<string name="vpn_might_be_needed">قد تكون هناك حاجة إلى فيبيان حتى يعمل هذا المزود بشكل صحيح</string>
<string name="normal_no_plot">لم يتم العثور على قطعة أرض</string>
<string name="torrent_no_plot">لم يتم العثور على وصف</string>
<string name="show_log_cat">🐈عرض لوجكات</string>
<string name="test_log">سجل</string>
<string name="picture_in_picture">صور في صور</string>
<string name="resume_time_left" formatted="true">%d
\nباقي</string>
</resources>

View file

@ -157,7 +157,7 @@
<string name="show_fillers_settings">Mostrar episódios de Filler em anime</string>
<string name="show_trailers_settings">Mostrar trailers</string>
<string name="kitsu_settings">Mostrar posters do Kitsu</string>
<string name="pref_filter_search_quality">Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa</string>
<string name="pref_filter_search_quality">Esconder qualidades de vídeo selecionadas nos resultados da pesquisa</string>
<string name="automatic_plugin_updates">Atualizações de plugin automáticas</string>
<string name="updates_settings">Mostrar atualizações do app</string>
<string name="updates_settings_des">Automaticamente procurar por novas atualizações ao abrir</string>
@ -222,7 +222,7 @@
<string name="movies_singular">Filme</string>
<string name="tv_series_singular">Série</string>
<string name="cartoons_singular">Desenho Animado</string>
<string name="anime_singular">@string/anime</string>
<string name="anime_singular">Anime</string>
<string name="ova_singular">@string/ova</string>
<string name="torrent_singular">Torrent</string>
<string name="documentaries_singular">Documentário</string>
@ -265,14 +265,14 @@
<string name="video_buffer_disk_settings">Cache do vídeo em disco</string>
<string name="video_buffer_clear_settings">Limpar cache de vídeo e imagem</string>
<string name="video_ram_description">Causará travamentos aleatórios se definido muito alto. Não mude caso tiver pouca memória RAM, como um Android TV ou um telefone antigo</string>
<string name="video_disk_description">Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV</string>
<string name="video_disk_description">Causa problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV.</string>
<string name="dns_pref">DNS sobre HTTPS</string>
<string name="dns_pref_summary">Útil para burlar bloqueios de provedores de internet</string>
<string name="add_site_pref">Clonar site</string>
<string name="remove_site_pref">Remover site</string>
<string name="add_site_summary">Adiciona um clone de um site existente, com uma URL diferente</string>
<string name="download_path_pref">Caminho para Download</string>
<string name="nginx_url_pref">Url do servidor Nginx</string>
<string name="nginx_url_pref">URL do servidor NGINX</string>
<string name="display_subbed_dubbed_settings">Mostrar Anime Dublado/Legendado</string>
<string name="resize_fit">Ajustar para a Tela</string>
<string name="resize_fill">Esticar</string>
@ -338,7 +338,7 @@
<string name="subtitles_shadow">Sombreado</string>
<string name="subtitles_raised">Em Relevo</string>
<string name="subtitle_offset">Sincronizar legendas</string>
<string name="subtitle_offset_hint">1000ms</string>
<string name="subtitle_offset_hint">1000 ms</string>
<string name="subtitle_offset_title">Atraso de legenda</string>
<string name="subtitle_offset_extra_hint_later_format">Use isto se as legendas forem mostradas %dms adiantadas</string>
<string name="subtitle_offset_extra_hint_before_format">Use isto se as legendas forem mostradas %dms atrasadas</string>
@ -382,9 +382,9 @@
<string name="resolution_and_title">Resolução e título</string>
<string name="title">Título</string>
<string name="resolution">Resolução</string>
<string name="error_invalid_id">Id invalida</string>
<string name="error_invalid_id">ID inválido</string>
<string name="error_invalid_data">Dado invalido</string>
<string name="error_invalid_url">URL invalido</string>
<string name="error_invalid_url">URL inválida</string>
<string name="error">Erro</string>
<string name="subtitles_remove_captions">Remover legendas ocultas(CC) das legendas</string>
<string name="subtitles_remove_bloat">Remover bloat das legendas</string>
@ -406,8 +406,8 @@
<string name="plugin_loaded">Plugin Carregado</string>
<string name="plugin_deleted">Plugin Apagado</string>
<string name="plugin_load_fail" formatted="true">Falha ao carregar %s</string>
<string name="batch_download_start_format" formatted="true">Iniciada a transferência %d %s</string>
<string name="batch_download_finish_format" formatted="true">Transferido %d %s com sucesso</string>
<string name="batch_download_start_format" formatted="true">Iniciada a transferência %d %s</string>
<string name="batch_download_finish_format" formatted="true">Transferido %d %s</string>
<string name="batch_download_nothing_to_download_format" formatted="true">Tudo %s já transferido</string>
<string name="batch_download">Transferência em batch</string>
<string name="plugin_singular">Plugin</string>
@ -444,7 +444,7 @@
<string name="browser">Navegador</string>
<string name="pref_category_backup">Copia de Segurança</string>
<string name="android_tv_interface_off_seek_settings_summary">A Barra de Progresso pode ser usada quando o player estiver oculto</string>
<string name="subscription_list_name">Inscrever</string>
<string name="subscription_list_name">Inscrito</string>
<string name="empty_library_logged_in_message">Essa lista está vazia. Tente mudar para outra.</string>
<string name="play_livestream_button">Reproduzir Livestream</string>
<string name="test_log">Log do Teste</string>
@ -493,10 +493,10 @@
\nNOTA: Se a soma for 10 ou mais, o Player pulará automaticamente o carregamento quando o link for carregado!</string>
<string name="safe_mode_file">Arquivo de modo de segurança encontrado!
\nNão carregar nenhuma extensão na inicialização até que o arquivo seja removido.</string>
<string name="subscription_new">Inscrevel em %d</string>
<string name="subscription_new">Inscrito em %s</string>
<string name="subscription_episode_released">Episódio %d Lançado</string>
<string name="set_default">Selecionar padrão</string>
<string name="subscription_deleted">Disinscrevel em %d</string>
<string name="subscription_deleted">Desinscrito de %s</string>
<string name="apk_installer_settings_des">Alguns aparelhos não possuem suporte para este pacote de instalação. Tente a opção legada se a atualização não instalar.</string>
<string name="mobile_data">Dados móveis</string>
<string name="profile_number">Perfil %d</string>
@ -550,4 +550,21 @@
<string name="audio_tracks">Faixas de áudio</string>
<string name="sort_updated_new">Adicionado em (novo para antigo)</string>
<string name="video_tracks">Faixas de video</string>
<string name="pref_category_subtitles">Legendas</string>
<string name="player_settings_play_in_browser">Navegador</string>
<string name="is_adult">18+</string>
<string name="pref_category_links">Links</string>
<string name="pref_category_player_features">Funcionalidades do Player</string>
<string name="apk_installer_settings">Instalador APK</string>
<string name="pref_category_looks">Aparência</string>
<string name="disable">Desativar</string>
<string name="use">Usar</string>
<string name="network_adress_example">Link da stream</string>
<string name="pref_category_gestures">Gestos</string>
<string name="plugin_downloaded">Plugin baixado</string>
<string name="jsdelivr_enabled">Não foi possível se conectar ao GitHub. Ativando proxy jsDelivr…</string>
<string name="pref_category_cache">Cache</string>
<string name="other_singular">Vídeo</string>
<string name="pref_category_android_tv">Android TV</string>
<string name="wifi">Wi-Fi</string>
</resources>

View file

@ -5,19 +5,19 @@
<string name="next_episode_format" formatted="true">Episode %d wird veröffentlicht in</string>
<string name="result_poster_img_des">Vorschaubild</string>
<string name="search_poster_img_des">Vorschaubild</string>
<string name="subs_hold_to_reset_to_default">Halten, um auf die Standardeinstellungen zurückzusetzen</string>
<string name="subs_hold_to_reset_to_default">Halten, um auf Standardeinstellungen zurückzusetzen</string>
<string name="restore_failed_format" formatted="true">Wiederherstellung der Daten aus der Datei %s fehlgeschlagen</string>
<string name="backup_success">Daten erfolgreich gesichert</string>
<string name="backup_failed_error_format">Fehler beim Sichern von %s</string>
<string name="no_chromecast_support_toast">Dieser Anbieter hat keine Chromecast-Unterstützung</string>
<string name="episode_action_chromecast_mirror">Chromecast-Mirror</string>
<string name="episode_action_play_in_app">In App wiedergeben</string>
<string name="skip_type_mixed_op">Vermischte Openings</string>
<string name="skip_type_mixed_op">Gemischte Openings</string>
<string name="skip_type_creddits">Abspann</string>
<string name="skip_type_intro">Intro</string>
<string name="clear_history">Verlauf löschen</string>
<string name="history">Verlauf</string>
<string name="enable_skip_op_from_database_des">Überspringen Knopf für Openings/Endings anzeigen</string>
<string name="enable_skip_op_from_database_des">Button zum Überspringen für Openings/Endings anzeigen</string>
<string name="clipboard_too_large">Zu viel Text. Kann nicht in der Zwischenablage gespeichert werden.</string>
<string name="episode_poster_img_des">Episodenvorschaubild</string>
<string name="home_main_poster_img_des">Medienvorschaubild</string>
@ -34,7 +34,7 @@
<string name="app_name">CloudStream</string>
<string name="play_with_app_name">Mit CloudStream abspielen</string>
<string name="title_home">Startseite</string>
<string name="title_search">Suchen</string>
<string name="title_search">Suche</string>
<string name="title_downloads">Downloads</string>
<string name="title_settings">Einstellungen</string>
<string name="search_hint">Suchen…</string>
@ -44,8 +44,8 @@
<string name="next_episode">Nächste Episode</string>
<string name="result_tags">Genres</string>
<string name="result_share">Teilen</string>
<string name="result_open_in_browser">In Browser öffnen</string>
<string name="skip_loading">Puffern überspringen</string>
<string name="result_open_in_browser">Im Browser öffnen</string>
<string name="skip_loading">Laden überspringen</string>
<string name="loading">Lädt…</string>
<string name="type_watching">Am schauen</string>
<string name="type_on_hold">Pausiert</string>
@ -79,7 +79,7 @@
<string name="popup_play_file">Datei abspielen</string>
<string name="popup_resume_download">Download fortsetzen</string>
<string name="popup_pause_download">Download pausieren</string>
<string name="pref_disable_acra">Automatische Fehlerberichterstattung deaktivieren</string>
<string name="pref_disable_acra">Automatische Fehlerberichtserstattung deaktivieren</string>
<string name="home_more_info">Mehr Infos</string>
<string name="home_expanded_hide">Verstecken</string>
<string name="home_play">Abspielen</string>
@ -106,8 +106,8 @@
<string name="subs_font_size">Schriftgröße</string>
<string name="search_provider_text_providers">Suche anhand Anbietern</string>
<string name="search_provider_text_types">Suche anhand Typen</string>
<string name="benene_count_text">%d Benenes an die Devs verteilt</string>
<string name="benene_count_text_none">Noch keine Benenes verteilt</string>
<string name="benene_count_text">%d Benenes an die Devs geschenkt</string>
<string name="benene_count_text_none">Noch keine Benenes verschenkt</string>
<string name="subs_auto_select_language">Sprache automatisch wählen</string>
<string name="subs_download_languages">Sprachen herunterladen</string>
<string name="subs_subtitle_languages">Untertitelsprache</string>
@ -117,8 +117,8 @@
<string name="action_open_watching">Mehr Infos</string>
<string name="action_open_play">@string/home_play</string>
<string name="vpn_might_be_needed">Damit dieser Anbieter korrekt funktioniert, ist möglicherweise ein VPN erforderlich</string>
<string name="vpn_torrent">Dieser Anbieter bietet Torrents an, ein VPN wird dringend empfohlen</string>
<string name="provider_info_meta">Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie auf der Website nicht vorhanden sind.</string>
<string name="vpn_torrent">Dieser Anbieter bietet Torrents an, ein VPN wird deswegen dringend empfohlen</string>
<string name="provider_info_meta">Metadaten werden nicht von der Website bereitgestellt, das Laden des Videos schlägt fehl, wenn sie nicht auf der Website vorhanden sind.</string>
<string name="torrent_plot">Beschreibung</string>
<string name="normal_no_plot">Keine Handlung gefunden</string>
<string name="torrent_no_plot">Keine Beschreibung gefunden</string>
@ -143,7 +143,7 @@
<string name="double_tap_to_pause_settings">Doppeltippen zum Pausieren</string>
<string name="double_tap_to_seek_amount_settings">Zeit für vor- und zurückspulen im Player (Sekunden)</string>
<string name="double_tap_to_seek_settings_des">Zweimal auf die rechte oder linke Seite tippen, um vor- oder zurückzuspulen</string>
<string name="double_tap_to_pause_settings_des">Doppelt in die Mitte tippen, um zu pausieren</string>
<string name="double_tap_to_pause_settings_des">Zweimal in die Mitte tippen, um zu pausieren</string>
<string name="use_system_brightness_settings">Systemhelligkeit verwenden</string>
<string name="use_system_brightness_settings_des">Systemhelligkeit anstelle eines dunklen Overlay im Player verwenden</string>
<string name="episode_sync_settings">Episodenfortschritt aktualisieren</string>
@ -163,7 +163,7 @@
<string name="show_fillers_settings">Füller-Episoden für Animes anzeigen</string>
<string name="show_trailers_settings">Trailer anzeigen</string>
<string name="kitsu_settings">Vorschaubilder von Kitsu anzeigen</string>
<string name="pref_filter_search_quality">Ausgewählte Videoqualität bei Suchergebnissen ausblenden</string>
<string name="pref_filter_search_quality">Ausgewählte Videoqualität in den Suchergebnissen ausblenden</string>
<string name="automatic_plugin_updates">Automatische Plugin-Updates</string>
<string name="updates_settings">App-Updates anzeigen</string>
<string name="updates_settings_des">Automatisches Suchen nach neuen Updates nach dem Start.</string>
@ -172,11 +172,11 @@
<string name="github">Github</string>
<string name="lightnovel">Light Novel App von denselben Entwicklern</string>
<string name="anim">Anime App von denselben Entwicklern</string>
<string name="discord">Discord beitreten</string>
<string name="benene">Eine Benene an die Devs verteilen</string>
<string name="benene_des">Verteilte Benenes</string>
<string name="discord">Trete dem Discord Server bei</string>
<string name="benene">Eine Benene an die Devs schenken</string>
<string name="benene_des">Geschenkte Benenes</string>
<string name="app_language">App-Sprache</string>
<string name="no_links_found_toast">Keine Verlinkung gefunden</string>
<string name="no_links_found_toast">Keine Links gefunden</string>
<string name="copy_link_toast">Link in die Zwischenablage kopiert</string>
<string name="play_episode_toast">Episode abspielen</string>
<string name="subs_default_reset_toast">Auf Standardwert zurücksetzen</string>
@ -240,7 +240,7 @@
<string name="remote_error">Remote-Fehler</string>
<string name="render_error">Renderfehler</string>
<string name="unexpected_error">Unerwarteter Playerfehler</string>
<string name="storage_error">Downloadfehler, Speicherberechtigungen prüfen</string>
<string name="storage_error">Downloadfehler, bitte überprüfen sie die Speicherberechtigungen</string>
<string name="episode_action_chromecast_episode">Chromecast-Episode</string>
<string name="episode_action_play_in_format">In %s wiedergeben</string>
<string name="episode_action_play_in_browser">In Browser wiedergeben</string>
@ -255,7 +255,7 @@
<string name="show_title">Titel</string>
<string name="poster_ui_settings">UI-Elemente auf Vorschaubild umschalten</string>
<string name="no_update_found">Kein Update gefunden</string>
<string name="check_for_update">Auf Update prüfen</string>
<string name="check_for_update">Auf Updates prüfen</string>
<string name="video_lock">Sperren</string>
<string name="video_aspect_ratio_resize">Skalieren</string>
<string name="video_source">Quelle</string>
@ -270,16 +270,16 @@
<string name="video_buffer_length_settings">Videopufferlänge</string>
<string name="video_buffer_disk_settings">Video-Cache in Speicher</string>
<string name="video_buffer_clear_settings">Video- und Bild-Cache leeren</string>
<string name="video_ram_description">Verursacht Abstürze, wenn zu hoch eingestellt. Nicht ändern, wenn wenig Arbeitsspeicher verfügbar ist, wie z.B. ein Android TV oder ein altes Telefon.</string>
<string name="video_disk_description">Kann auf Systemen mit geringem Speicherplatz, wie z. B. Android TV-Geräten, zu Problemen führen, wenn der Wert zu hoch eingestellt ist.</string>
<string name="video_ram_description">Verursacht Abstürze, wenn zu hoch eingestellt. Nicht ändern, wenn wenig Arbeitsspeicher verfügbar ist, wie z.B. auf einem Android TV oder auf einem alten Smartphone.</string>
<string name="video_disk_description">Kann auf Systemen mit geringem Speicherplatz, wie z. B. auf Android TV-Geräten, zu Problemen führen, wenn der Wert zu hoch eingestellt ist.</string>
<string name="dns_pref">DNS über HTTPS</string>
<string name="dns_pref_summary">Nützlich für die Umgehung von ISP-Sperren</string>
<string name="dns_pref_summary">Nützlich zur Umgehung von ISP-Sperren</string>
<string name="add_site_pref">Website klonen</string>
<string name="remove_site_pref">Website entfernen</string>
<string name="add_site_summary">Einen Klon einer bestehenden Website mit einer anderen URL hinzufügen</string>
<string name="download_path_pref">Downloadpfad</string>
<string name="nginx_url_pref">Nginx-Server-URL</string>
<string name="display_subbed_dubbed_settings">Dubbed/Subbed Anime anzeigen (Synchronisiert/Untertitelt)</string>
<string name="display_subbed_dubbed_settings">Dubbed/Subbed Anime anzeigen</string>
<string name="resize_fit">An Bildschirm anpassen</string>
<string name="resize_fill">Strecken</string>
<string name="resize_zoom">Vergrößern</string>
@ -308,7 +308,7 @@
<string name="example_ip">127.0.0.1</string>
<string name="example_site_name">MeineCooleSeite</string>
<string name="example_site_url">example.com</string>
<string name="example_lang_name">Sprachcode (en)</string>
<string name="example_lang_name">Sprachencode (en)</string>
<string name="login_format" formatted="true">%s %s</string>
<string name="account">Account</string>
<string name="logout">Ausloggen</string>
@ -317,13 +317,13 @@
<string name="add_account">Account hinzufügen</string>
<string name="create_account">Account erstellen</string>
<string name="add_sync">Synchronisation hinzufügen</string>
<string name="added_sync_format" formatted="true">Hinzugefügt %s</string>
<string name="added_sync_format" formatted="true">%s hinzugefügt</string>
<string name="upload_sync">Sync</string>
<string name="sync_score">Bewertung</string>
<string name="sync_score_format" formatted="true">%d / 10</string>
<string name="sync_total_episodes_none">/\?\?</string>
<string name="sync_total_episodes_some" formatted="true">/%d</string>
<string name="authenticated_user" formatted="true">Authentifiziert %s</string>
<string name="authenticated_user" formatted="true">%s authentifiziert</string>
<string name="authenticated_user_fail" formatted="true">Die Authentifizierung bei %s ist fehlgeschlagen</string>
<string name="none">Keine</string>
<string name="normal">Normal</string>
@ -335,10 +335,10 @@
<string name="subtitles_shadow">Schatten</string>
<string name="subtitles_raised">Erhöht</string>
<string name="subtitle_offset">Untertitel synchronisieren</string>
<string name="subtitle_offset_hint">1000ms</string>
<string name="subtitle_offset_hint">1000 ms</string>
<string name="subtitle_offset_title">Untertitelverzögerung</string>
<string name="subtitle_offset_extra_hint_later_format">Verwenden, wenn die Untertitel %dms zu früh angezeigt werden</string>
<string name="subtitle_offset_extra_hint_before_format">Verwenden, wenn die Untertitel %dms zu spät angezeigt werden</string>
<string name="subtitle_offset_extra_hint_later_format">Verwenden, wenn die Untertitel %d ms zu früh angezeigt werden</string>
<string name="subtitle_offset_extra_hint_before_format">Verwenden, wenn die Untertitel %d ms zu spät angezeigt werden</string>
<string name="subtitle_offset_extra_hint_none_format">Keine Untertitelverzögerung</string>
<string name="subtitles_example_text">Vogel Quax zwickt Johnys Pferd Bim</string>
<string name="recommended">Empfohlen</string>
@ -359,7 +359,7 @@
<string name="quality_hd">HD</string>
<string name="quality_ts">TS</string>
<string name="quality_tc">TC</string>
<string name="quality_blueray">BlueRay</string>
<string name="quality_blueray">Blue-ray</string>
<string name="quality_workprint">WP</string>
<string name="quality_dvd">DVD</string>
<string name="quality_4k">4K</string>
@ -408,7 +408,7 @@
<string name="plugin">Plugins</string>
<string name="delete_repository_plugins">Dadurch werden auch alle Repository-Plugins gelöscht</string>
<string name="delete_repository">Repository löschen</string>
<string name="setup_extensions_subtext">Lade eine Liste der Websiten herunter, welche du verwenden möchtest</string>
<string name="setup_extensions_subtext">Lade eine Liste der Websites herunter, welche du verwenden möchtest</string>
<string name="plugins_downloaded" formatted="true">Heruntergeladen: %d</string>
<string name="plugins_disabled" formatted="true">Deaktiviert: %d</string>
<string name="plugins_not_downloaded" formatted="true">Nicht heruntergeladen: %d</string>
@ -416,7 +416,7 @@
\n
\nAufgrund eines hirnlosen DMCA-Takedowns durch Sky UK Limited 🤮 können wir die Repository-Site nicht in der App verlinken.
\n
\nTrete unserem Discord bei oder suche online.</string>
\nTrete unserem Discord Server bei oder suche online.</string>
<string name="view_public_repositories_button">Community-Repositories anzeigen</string>
<string name="view_public_repositories_button_short">Öffentliche Liste</string>
<string name="uppercase_all_subtitles">Alle Untertitel in Großbuchstaben</string>
@ -427,7 +427,7 @@
<string name="video_tracks">Videospuren</string>
<string name="apply_on_restart">Bei Neustart anwenden</string>
<string name="safe_mode_title">Abgesicherter Modus aktiviert</string>
<string name="safe_mode_description">Alle Erweiterungen wurden aufgrund eines Absturzes deaktiviert, damit Sie diejenige finden können, die Probleme verursacht.</string>
<string name="safe_mode_description">Alle Erweiterungen wurden aufgrund eines Absturzes deaktiviert, damit Sie diejenige finden können, welche Probleme verursacht.</string>
<string name="safe_mode_crash_info">Absturzinfo ansehen</string>
<string name="extension_rating" formatted="true">Bewertung: %s</string>
<string name="extension_description">Beschreibung</string>
@ -460,7 +460,7 @@
<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="apk_installer_settings">APK-Installer</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="apk_installer_settings_des">Einige Smartphones 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="pref_category_links">Links</string>
<string name="pref_category_app_updates">App-Updates</string>
@ -482,7 +482,7 @@
<string name="no">Nein</string>
<string name="update_notification_downloading">App-Update wird heruntergeladen…</string>
<string name="update_notification_installing">App-Update wird installiert…</string>
<string name="update_notification_failed">Konnte die neue Version der App nicht installieren</string>
<string name="update_notification_failed">Die neue Version der App konnte nicht installieren werden</string>
<string name="apk_installer_legacy">Legacy</string>
<string name="apk_installer_package_installer">PackageInstaller</string>
<string name="update_started">Aktualisierung gestartet</string>
@ -493,18 +493,18 @@
<string name="browser">Browser</string>
<string name="sort_by">Sortieren nach</string>
<string name="sort">Sortieren</string>
<string name="sort_rating_desc">Bewertung (gut bis schlecht)</string>
<string name="sort_rating_asc">Bewertung (schlecht bis gut)</string>
<string name="sort_updated_new">Aktualisiert (neu bis alt)</string>
<string name="sort_updated_old">Aktualisiert (alt bis neu)</string>
<string name="sort_alphabetical_a">Alphabetisch (A bis Z)</string>
<string name="sort_alphabetical_z">Alphabetisch (Z bis A)</string>
<string name="sort_rating_desc">Bewertung (gut zu schlecht)</string>
<string name="sort_rating_asc">Bewertung (schlecht zu gut)</string>
<string name="sort_updated_new">Aktualisiert (neu zu alt)</string>
<string name="sort_updated_old">Aktualisiert (alt zu neu)</string>
<string name="sort_alphabetical_a">Alphabetisch (A zu Z)</string>
<string name="sort_alphabetical_z">Alphabetisch (Z zu A)</string>
<string name="select_library">Bibliothek auswählen</string>
<string name="open_with">Öffnen mit</string>
<string name="empty_library_no_accounts_message">Deine Bibliothek ist leer :(
\nMelde dich mit einem Bibliothekskonto an oder füge Titel zu deiner lokalen Bibliothek hinzu.</string>
<string name="empty_library_logged_in_message">Diese Liste ist leer. Versuche zu einer anderen Liste zu wechseln.</string>
<string name="safe_mode_file">Datei für abgesicherten Modus gefunden!
<string name="empty_library_logged_in_message">Diese Liste ist leer. Versuch zu einer anderen Liste zu wechseln.</string>
<string name="safe_mode_file">Datei für den abgesicherten Modus gefunden!
\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>
@ -549,7 +549,7 @@
<string name="automatic_plugin_download_mode_title">Filtermodus für Plugin-Downloads auswählen</string>
<string name="already_voted">Es wurde bereits abgestimmt</string>
<string name="no_plugins_found_error">Keine Plugins im Repository gefunden</string>
<string name="no_repository_found_error">Repository nicht gefunden, überprüfe die URL und probiere eine VPN</string>
<string name="unable_to_inflate">Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein schwerwiegender Fehler und sollte sofort gemeldet werden. %s</string>
<string name="no_repository_found_error">Repository nicht gefunden, überprüf die URL und versuch ein VPN</string>
<string name="unable_to_inflate">Die Benutzeroberfläche konnte nicht korrekt erstellt werden. Dies ist ein SCHWERWIEGENDER FEHLER und sollte sofort gemeldet werden. %s</string>
<string name="disable">Deaktivieren</string>
</resources>

View file

@ -540,7 +540,7 @@
\n
\nREMARQUE: Si la somme est de 10 ou plus, le joueur sautera automatiquement le chargement lorsque ce lien est chargé!</string>
<string name="no_plugins_found_error">Aucun plugin trouvé dans ce dossier</string>
<string name="no_repository_found_error">Dossier non trouvé, vérifiez l\'url et essayé un VPN</string>
<string name="no_repository_found_error">Dépôt introuvable, vérifiez l\'URL et essayez avec un VPN</string>
<string name="mobile_data">Données mobiles</string>
<string name="set_default">Définir par défaut</string>
<string name="use">Utiliser</string>
@ -552,4 +552,6 @@
<string name="qualities">Qualités</string>
<string name="unable_to_inflate">L\'interface utilisateur n\'a pas pu être créée correctement. Il s\'agit d\'un bogue majeur qui doit être signalé immédiatement %s</string>
<string name="automatic_plugin_download_mode_title">Sélectionnez le mode pour filtrer le téléchargement des plugins</string>
<string name="profile_background_des">Fond de profil</string>
<string name="default_account">@string/default_subtitles</string>
</resources>

View file

@ -566,4 +566,15 @@
<string name="profile_background_des">Pozadina profila</string>
<string name="unable_to_inflate">Nije bilo moguće ispravno izraditi korisničko sučelje. Ovo je ZNAČAJNA GREŠKA i treba se odmah prijaviti %s</string>
<string name="automatic_plugin_download_mode_title">Odaberi modus za filtriranje preuzimanja dodataka</string>
<string name="disable">Onemogući</string>
<string name="default_account">@string/default_subtitles</string>
<string name="no_plugins_found_error">U repozitoriju nisu pronađeni dodaci</string>
<string name="no_repository_found_error">Repozitorij nije pronađen, provjerite URL i pokušajte koristiti VPN</string>
<string name="quality_profile_help">Ovdje možete promijeniti način na koji su izvori poredani. Ako video ima viši prioritet, pojavit će se više u odabiru izvora. Zbroj prioriteta izvora i prioriteta kvalitete je video prioritet.
\n
\nIzvor A: 3
\nKvaliteta B: 7
\nImat će kombinirani prioritet videozapisa od 10.
\n
\nNAPOMENA: Ako je zbroj 10 ili više, video player će automatski preskočiti učitavanje kada se ta poveznica učita!</string>
</resources>

View file

@ -219,7 +219,7 @@
<string name="status_ongoing">Folyamatban levő</string>
<string name="year">Év</string>
<string name="site">Webhely</string>
<string name="synopsis">Szinopszis</string>
<string name="synopsis">Összegzés</string>
<string name="no_subtitles">Nincsenek feliratok</string>
<string name="remote_error">Távoli hiba</string>
<string name="render_error">Render hiba</string>
@ -237,7 +237,7 @@
<string name="swipe_to_change_settings_des">Csúsztassa felfelé vagy lefelé a bal vagy jobb oldalon a fényerő vagy a hangerő megváltoztatásához</string>
<string name="backup_settings">Biztonsági mentés</string>
<string name="benene_count_text_none">0 Banán a fejlesztőknek</string>
<string name="swipe_to_seek_settings">Húzás a kereséshez</string>
<string name="swipe_to_seek_settings">Húzd el, hogy beless</string>
<string name="autoplay_next_settings">Következő epizód automatikus lejátszása</string>
<string name="autoplay_next_settings_des">Következő epizód lejátszása amikor az aktuális epizód véget ér</string>
<string name="double_tap_to_seek_settings">Dupla koppintás a kereséshez</string>
@ -510,4 +510,5 @@
<string name="tv_layout">TV elrendezés</string>
<string name="automatic">Automatikus</string>
<string name="android_tv_interface_on_seek_settings_summary">Az átugrás mértéke, amikor a lejátszó látható</string>
<string name="automatic_plugin_download_mode_title">Válassza ki a módot a pluginek letöltésének szűréséhez</string>
</resources>

View file

@ -153,4 +153,9 @@
<string name="updates_settings">ଆପ୍ ଅଦ୍ୟତନ ଦେଖାଇବା</string>
<string name="update_started">ଅଦ୍ୟତନ ଆରମ୍ଭ ହୋଇଛି</string>
<string name="search_hint">ସନ୍ଧାନ କରିବା…</string>
<string name="skip_type_recap">ସଂକ୍ଷିପ୍ତବୃତ୍ତି</string>
<string name="play_movie_button">ଚଳଚ୍ଚିତ୍ର ଚଲାଅ</string>
<string name="search_hint_site" formatted="true">%s ସନ୍ଧାନ କରିବା…</string>
<string name="next_episode">ପରବର୍ତ୍ତୀ ଅଧ୍ୟାୟ</string>
<string name="no_data">କୌଣସି ତଥ୍ୟ ନାହିଁ</string>
</resources>

View file

@ -570,4 +570,5 @@
<string name="unable_to_inflate">UI nu a putut fi creată corect, acesta este un BUG MAJOR și trebuie raportat imediat %s</string>
<string name="automatic_plugin_download_mode_title">Selectați modul de filtrare a descărcării plugin-urilor</string>
<string name="default_account">@string/default_subtitles</string>
<string name="already_voted">Ați votat deja</string>
</resources>

View file

@ -6,12 +6,12 @@
<string name="title_home">முகப்பு</string>
<string name="title_search">தேடு</string>
<string name="title_downloads">பதிவிறக்கம்</string>
<string name="no_data">கவல் எதுவும் இல்லை</string>
<string name="no_data">ரவு இல்லை</string>
<string name="episode_more_options_des">மேலும் விருப்பங்கள்</string>
<string name="next_episode">அடுத்த எபிசோட</string>
<string name="next_episode">அடுத்த அத்தியாயம</string>
<string name="result_tags">வகைகள்</string>
<string name="result_share">பகிர்</string>
<string name="result_open_in_browser">Browser இல் திற</string>
<string name="result_open_in_browser">உலாவியில் திற</string>
<string name="skip_loading">ஏற்றுவதைத் தவிர்</string>
<string name="type_watching">பார்த்து கொண்டிருப்பது</string>
<string name="type_on_hold">நிறுத்தி வைக்கப்பட்டுள்ளது</string>
@ -21,9 +21,9 @@
<string name="play_torrent_button">ஸ்ட்ரீம் டோரண்ட்</string>
<string name="pick_subtitle">வசன வரிகள்</string>
<string name="go_back">பின் செல்</string>
<string name="play_episode">எபிசோடை இயக்கு</string>
<string name="play_episode">அத்தியாயத்தை இயக்கு</string>
<string name="download">எபிசோட் பதிவிற்கான அனுமதி கொடுக்கவும்</string>
<string name="downloaded">பதிவிறக்கம் செய்யப்பட்டது</string>
<string name="downloaded">பதிவிறக்கப்பட்டது</string>
<string name="downloading">பதிவிறக்குகிறது</string>
<string name="download_paused">பதிவிறக்கம் இடைநிறுத்தப்பட்டது</string>
<string name="download_started">பதிவிறக்கம் தொடங்கியது</string>
@ -67,10 +67,10 @@
<string name="loading">ஏற்றுகிறது…</string>
<string name="type_dropped">கைவிடப்பட்டது</string>
<string name="download_done">பதிவிறக்கம் முடிந்தது</string>
<string name="reload_error">இணைப்பை மீண்டும் முயற்சிக்கவும்…</string>
<string name="reload_error">இணைப்பை மீண்டும் முயவும்…</string>
<string name="play_movie_button">திரைப்படத்தை இயக்கு</string>
<string name="play_livestream_button">லைவ்ஸ்ட்ரீம் இயக்கு</string>
<string name="play_trailer_button">டிரெய்லரை இயக்கவும்</string>
<string name="play_trailer_button">டிரெய்லரை இயக்க</string>
<string name="pick_source">மூலம்</string>
<string name="error_loading_links_toast">இணைப்புகளை ஏற்றுவதில் பிழை</string>
<string name="home_play">இயக்கு</string>
@ -107,4 +107,14 @@
<string name="double_tap_to_pause_settings">இடைநிறுத்துவதற்கு இருமுறை தட்டவும்</string>
<string name="chromecast_subtitles_settings_des">Chromecast வசன அமைப்புகள்</string>
<string name="use_system_brightness_settings_des">இருண்ட மேலடுக்குக்குப் பதிலாக ஆப் பிளேயரில் சிஸ்டம் பிரகாசத்தைப் பயன்படுத்தவும்</string>
<string name="next_episode_format" formatted="true">அத்தியாயம் %d-இன் வெளியீட்டு நேரம்</string>
<string name="next_episode_time_hour_format" formatted="true">%dம %dநி</string>
<string name="next_episode_time_min_format" formatted="true">%dநி</string>
<string name="home_next_random_img_des">அடுத்து ஏதாவது</string>
<string name="browser">உலாவி</string>
<string name="duration_format" formatted="true">%d நிமி</string>
<string name="play_with_app_name">CloudStream-உடன் இயக்கு</string>
<string name="new_update_format" formatted="true">புதிய புதுப்பிப்பு உள்ளது
\n%s-&gt;%s</string>
<string name="filler" formatted="true">நிரப்பி</string>
</resources>

View file

@ -18,7 +18,7 @@
<string name="preview_background_img_des">Попередній перегляд фону</string>
<string name="player_speed_text_format" formatted="true">Швидкість (%.2fx)</string>
<string name="new_update_format" formatted="true">Знайдено нове оновлення!
\n%s -&gt; %s</string>
\n%s &gt; %s</string>
<string name="title_search">Пошук</string>
<string name="title_downloads">Завантаження</string>
<string name="duration_format" formatted="true">%d хв</string>
@ -37,7 +37,7 @@
<string name="type_dropped">Покинуто</string>
<string name="play_movie_button">Переглянути фільм</string>
<string name="play_trailer_button">Переглянути трейлер</string>
<string name="play_torrent_button">Трансляція через торрент</string>
<string name="play_torrent_button">Трансляція через торент</string>
<string name="reload_error">Повторити підключення…</string>
<string name="go_back">Назад</string>
<string name="play_episode">Переглянути епізод</string>
@ -75,7 +75,7 @@
<string name="continue_watching">Продовжити перегляд</string>
<string name="action_remove_watching">Вилучити</string>
<string name="action_open_watching">Детальніше</string>
<string name="vpn_torrent">Цей постачальник є торрентом, рекомендується VPN</string>
<string name="vpn_torrent">Цей постачальник є торентом, рекомендується використовувати VPN</string>
<string name="torrent_plot">Опис</string>
<string name="normal_no_plot">Сюжет не знайдено</string>
<string name="torrent_no_plot">Опис не знайдено</string>
@ -86,9 +86,9 @@
<string name="chromecast_subtitles_settings">Субтитри Chromecast</string>
<string name="chromecast_subtitles_settings_des">Налаштування субтитрів Chromecast</string>
<string name="eigengraumode_settings">Режим Eigengravy</string>
<string name="swipe_to_change_settings">Проведіть пальцем, щоб змінити налаштування</string>
<string name="swipe_to_change_settings">Проведіть, щоб змінити налаштування</string>
<string name="swipe_to_change_settings_des">Проведіть вгору або вниз з лівого або правого боку, щоб змінити яскравість чи гучність</string>
<string name="autoplay_next_settings_des">Відтворювати наступний епізод після закінчення поточного</string>
<string name="autoplay_next_settings_des">Відтворює наступний епізод після закінчення поточного</string>
<string name="title_home">Головна</string>
<string name="app_name">CloudStream</string>
<string name="filler" formatted="true">Філер</string>
@ -130,7 +130,7 @@
<string name="picture_in_picture">Картинка в картинці</string>
<string name="player_subtitles_settings_des">Налаштування субтитрів плеєра</string>
<string name="eigengraumode_settings_des">Додає опцію керування швидкістю в плеєрі</string>
<string name="swipe_to_seek_settings">Проведіть пальцем, щоб перемотати</string>
<string name="swipe_to_seek_settings">Проведіть, щоб перемотати</string>
<string name="double_tap_to_seek_settings">Двічі торкніться, щоб перемотати</string>
<string name="double_tap_to_pause_settings">Двічі торкніться для паузи</string>
<string name="double_tap_to_seek_amount_settings">Крок перемотки (секунди)</string>
@ -224,7 +224,7 @@
<string name="double_tap_to_seek_settings_des">Двічі торкніться праворуч або ліворуч, щоб перемотати відео вперед або назад</string>
<string name="use_system_brightness_settings_des">Використовуйте системну яскравість у плеєрі замість темної накладки</string>
<string name="restore_success">Завантажено файл резервної копії</string>
<string name="torrent">Торренти</string>
<string name="torrent">Торенти</string>
<string name="episode_sync_settings_des">Автоматична синхронізація прогресу поточного епізоду</string>
<string name="backup_failed">Відсутні дозволи на зберігання. Будь ласка, спробуйте ще раз.</string>
<string name="kitsu_settings">Показувати постери від Kitsu</string>
@ -256,7 +256,7 @@
<string name="nsfw">NSFW</string>
<string name="movies_singular">Фільм</string>
<string name="ova_singular">OVA</string>
<string name="torrent_singular">Торрент</string>
<string name="torrent_singular">Торент</string>
<string name="show_hd">Мітка якості</string>
<string name="nsfw_singular">NSFW</string>
<string name="episode_action_play_in_browser">Переглянути в браузері</string>
@ -294,9 +294,9 @@
<string name="primary_color_settings">Основний колір</string>
<string name="app_theme_settings">Тема застосунку</string>
<string name="bottom_title_settings">Розташування назви постера</string>
<string name="bottom_title_settings_des">Розмістіть назву під постером</string>
<string name="bottom_title_settings_des">Розмістити назву під постером</string>
<string name="example_password">Пароль123</string>
<string name="example_username">Моє круте ім\'я</string>
<string name="example_username">Моє круте імя</string>
<string name="example_email">hello@world.com</string>
<string name="example_site_name">Мій крутий сайт</string>
<string name="example_lang_name">Код мови (uk)</string>
@ -348,9 +348,9 @@
<string name="limit_title_rez">Роздільна здатність відеоплеєра</string>
<string name="video_buffer_length_settings">Довжина буфера відео</string>
<string name="video_buffer_clear_settings">Очистити кеш відео та зображень</string>
<string name="video_ram_description">Спричиняє збої, якщо встановлено занадто високе значення на пристроях із малим об\'ємом пам\'яті, наприклад Android TV.</string>
<string name="video_ram_description">Спричиняє збої, якщо встановлено занадто високе значення на пристроях із малим об’ємом пам’яті, наприклад Android TV.</string>
<string name="dns_pref_summary">Корисно для обходу блокувань провайдера</string>
<string name="video_disk_description">Спричиняє збої, якщо встановлено занадто високе значення на пристроях із малим об\'ємом вільної пам\'яті, наприклад Android TV.</string>
<string name="video_disk_description">Спричиняє збої, якщо встановлено занадто високе значення на пристроях із малим об’ємом вільної пам’яті, наприклад Android TV.</string>
<string name="dns_pref">DNS через HTTPS</string>
<string name="download_path_pref">Шлях завантаження</string>
<string name="add_site_summary">Додайте клон існуючого сайту, з іншою URL-адресою</string>
@ -374,10 +374,10 @@
<string name="sync_score">Оцінений</string>
<string name="player_load_subtitles">Завантажити з файлу</string>
<string name="max">Макс.</string>
<string name="subtitles_example_text">Щастям б\'єш жук їх глицю в фон й ґедзь пріч</string>
<string name="subtitles_example_text">Щастям бєш жук їх глицю в фон й ґедзь пріч</string>
<string name="subtitle_offset_hint">1000 мс</string>
<string name="subtitle_offset_extra_hint_later_format">Використовуйте цей параметр, якщо субтитри з\'являються на %d мс занадто рано</string>
<string name="subtitle_offset_extra_hint_before_format">Використовуйте це, якщо субтитри з\'являються із запізненням на %d мс</string>
<string name="subtitle_offset_extra_hint_later_format">Використовуйте цей параметр, якщо субтитри зявляються на %d мс занадто рано</string>
<string name="subtitle_offset_extra_hint_before_format">Використовуйте це, якщо субтитри зявляються із запізненням на %d мс</string>
<string name="player_loaded_subtitles" formatted="true">Завантажено %s</string>
<string name="actor_supporting">Підтримка</string>
<string name="actor_background">Фон</string>
@ -507,8 +507,8 @@
<string name="safe_mode_file">Файл безпечного режиму знайдено!
\nРозширеня не завантажуються під час запуску, доки файл не буде видалено.</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_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>
@ -532,7 +532,7 @@
<string name="set_default">Встановити за замовчуванням</string>
<string name="profiles">Профілі</string>
<string name="help">Допомога</string>
<string name="quality_profile_help">Тут ви можете змінити порядок джерел. Якщо відео має вищий пріоритет, воно з\'явиться вище у списку джерел. Сума пріоритету джерела та пріоритету якості є пріоритетом відео.
<string name="quality_profile_help">Тут ви можете змінити порядок джерел. Якщо відео має вищий пріоритет, воно зявиться вище у списку джерел. Сума пріоритету джерела та пріоритету якості є пріоритетом відео.
\n
\nДжерело A: 3
\nЯкість B: 7

View file

@ -0,0 +1 @@
- ପରିବର୍ତ୍ତନ ପୋଥି ଯୋଡ଼ାଗଲା!

View file

@ -1 +1 @@
- Додано журнал змін!
Додано журнал змін!