From 3ecaf47c9e28d0908e7729617059f88ff69e5c1c Mon Sep 17 00:00:00 2001 From: Stormunblessed <86633626+Stormunblessed@users.noreply.github.com> Date: Sat, 21 Jan 2023 03:25:06 -0600 Subject: [PATCH 01/47] fix fastream, tomatomatela, and added okrulink (#320) --- .../cloudstream3/extractors/Fastream.kt | 45 ++++++++----- .../cloudstream3/extractors/Okrulink.kt | 39 +++++++++++ .../cloudstream3/extractors/Tomatomatela.kt | 67 +++++++++++++------ .../cloudstream3/utils/ExtractorApi.kt | 2 + 4 files changed, 116 insertions(+), 37 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/Okrulink.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt index f813d7ea..e8f8c49a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Fastream.kt @@ -5,35 +5,50 @@ import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 +import com.lagradost.cloudstream3.utils.getAndUnpack +import org.jsoup.nodes.Document open class Fastream: ExtractorApi() { override var mainUrl = "https://fastream.to" override var name = "Fastream" override val requiresReferer = false - - - override suspend fun getUrl(url: String, referer: String?): List? { - val id = Regex("emb\\.html\\?(.*)\\=(enc|)").find(url)?.destructured?.component1() ?: return emptyList() - val sources = mutableListOf() - val response = app.post("$mainUrl/dl", - data = mapOf( - Pair("op","embed"), - Pair("file_code",id), - Pair("auto","1") - )).document + suspend fun getstream( + response: Document, + sources: ArrayList): Boolean{ response.select("script").amap { script -> - if (script.data().contains("sources")) { - val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)") - val m3u8 = m3u8regex.find(script.data())?.value ?: return@amap + if (script.data().contains(Regex("eval\\(function\\(p,a,c,k,e,[rd]"))) { + val unpacked = getAndUnpack(script.data()) + //val m3u8regex = Regex("((https:|http:)\\/\\/.*\\.m3u8)") + val newm3u8link = unpacked.substringAfter("file:\"").substringBefore("\"") + //val m3u8link = m3u8regex.find(unpacked)?.value ?: return@forEach generateM3u8( name, - m3u8, + newm3u8link, mainUrl ).forEach { link -> sources.add(link) } } } + return true + } + + override suspend fun getUrl(url: String, referer: String?): List { + val sources = ArrayList() + val idregex = Regex("emb.html\\?(.*)=") + if (url.contains(Regex("(emb.html.*fastream)"))) { + val id = idregex.find(url)?.destructured?.component1() ?: "" + val response = app.post("https://fastream.to/dl", allowRedirects = false, + data = mapOf( + "op" to "embed", + "file_code" to id, + "auto" to "1" + ) + ).document + getstream(response, sources) + } + val response = app.get(url, referer = url).document + getstream(response, sources) return sources } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Okrulink.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Okrulink.kt new file mode 100644 index 00000000..37bb09e3 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Okrulink.kt @@ -0,0 +1,39 @@ +package com.lagradost.cloudstream3.extractors + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities + +data class Okrulinkdata ( + @JsonProperty("status" ) var status : String? = null, + @JsonProperty("url" ) var url : String? = null +) + +open class Okrulink: ExtractorApi() { + override var mainUrl = "https://okru.link" + override var name = "Okrulink" + override val requiresReferer = false + + override suspend fun getUrl(url: String, referer: String?): List { + val sources = mutableListOf() + val key = url.substringAfter("html?t=") + val request = app.post("https://apizz.okru.link/decoding", allowRedirects = false, + data = mapOf("video" to key) + ).parsedSafe() + if (request?.url != null) { + sources.add( + ExtractorLink( + name, + name, + request.url!!, + "", + Qualities.Unknown.value, + isM3u8 = false + ) + ) + } + return sources + } +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Tomatomatela.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Tomatomatela.kt index 20bd69ba..28a2eb20 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Tomatomatela.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Tomatomatela.kt @@ -1,41 +1,64 @@ package com.lagradost.cloudstream3.extractors -import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.app +import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.USER_AGENT +import com.lagradost.cloudstream3.mapper import com.lagradost.cloudstream3.utils.AppUtils.parseJson -import com.lagradost.cloudstream3.utils.ExtractorApi -import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.Qualities + class Cinestart: Tomatomatela() { - override var name = "Cinestart" - override var mainUrl = "https://cinestart.net" + override var name: String = "Cinestart" + override val mainUrl: String = "https://cinestart.net" override val details = "vr.php?v=" } +class TomatomatelalClub: Tomatomatela() { + override var name: String = "Tomatomatela" + override val mainUrl: String = "https://tomatomatela.club" +} + open class Tomatomatela : ExtractorApi() { override var name = "Tomatomatela" - override var mainUrl = "https://tomatomatela.com" + override val mainUrl = "https://tomatomatela.com" override val requiresReferer = false private data class Tomato ( @JsonProperty("status") val status: Int, - @JsonProperty("file") val file: String + @JsonProperty("file") val file: String? ) open val details = "details.php?v=" + open val embeddetails = "/embed.html#" override suspend fun getUrl(url: String, referer: String?): List? { - val link = url.replace("$mainUrl/embed.html#","$mainUrl/$details") - val server = app.get(link, allowRedirects = false).text - val json = parseJson(server) - if (json.status == 200) return listOf( - ExtractorLink( - name, - name, - json.file, - "", - Qualities.Unknown.value, - isM3u8 = false + val link = url.replace("$mainUrl$embeddetails","$mainUrl/$details") + val sources = ArrayList() + val server = app.get(link, allowRedirects = false, + headers = mapOf( + "User-Agent" to USER_AGENT, + "Accept" to "application/json, text/javascript, */*; q=0.01", + "Accept-Language" to "en-US,en;q=0.5", + "X-Requested-With" to "XMLHttpRequest", + "DNT" to "1", + "Connection" to "keep-alive", + "Sec-Fetch-Dest" to "empty", + "Sec-Fetch-Mode" to "cors", + "Sec-Fetch-Site" to "same-origin" + ) - ) - return null + ).parsedSafe() + if (server?.file != null) { + sources.add( + ExtractorLink( + name, + name, + server.file, + "", + Qualities.Unknown.value, + isM3u8 = false + ) + ) + } + return sources } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 3978eb62..73603964 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -260,9 +260,11 @@ val extractorApis: MutableList = arrayListOf( UpstreamExtractor(), Tomatomatela(), + TomatomatelalClub(), Cinestart(), OkRu(), OkRuHttps(), + Okrulink(), // dood extractors DoodCxExtractor(), From c058409f9db78f7f9b69515bad71803240232389 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 21 Jan 2023 10:28:35 +0100 Subject: [PATCH 02/47] Translated using Weblate (Somali) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 17.9% (102 of 567 strings) Translated using Weblate (Dutch) Currently translated at 79.3% (450 of 567 strings) Translated using Weblate (Ukrainian) Currently translated at 99.8% (566 of 567 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Portuguese) Currently translated at 16.6% (94 of 566 strings) Translated using Weblate (Romanian) Currently translated at 74.9% (424 of 566 strings) Added translation using Weblate (Portuguese) Translated using Weblate (Ukrainian) Currently translated at 99.8% (565 of 566 strings) Translated using Weblate (Somali) Currently translated at 16.0% (91 of 566 strings) Added translation using Weblate (Somali) Translated using Weblate (Ukrainian) Currently translated at 100.0% (566 of 566 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (566 of 566 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 93.6% (530 of 566 strings) Translated using Weblate (German) Currently translated at 100.0% (566 of 566 strings) Translated using Weblate (Turkish) Currently translated at 96.6% (547 of 566 strings) Translated using Weblate (Dutch) Currently translated at 79.5% (450 of 566 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (566 of 566 strings) Translated using Weblate (Russian) Currently translated at 49.4% (280 of 566 strings) Translated using Weblate (Bengali) Currently translated at 40.8% (231 of 566 strings) Translated using Weblate (Russian) Currently translated at 40.4% (229 of 566 strings) Translated using Weblate (Greek) Currently translated at 100.0% (566 of 566 strings) Translated using Weblate (Ukrainian) Currently translated at 99.8% (565 of 566 strings) Translated using Weblate (Russian) Currently translated at 40.2% (228 of 566 strings) Translated using Weblate (Russian) Currently translated at 35.5% (201 of 566 strings) Translated using Weblate (Russian) Currently translated at 31.0% (176 of 566 strings) Co-authored-by: Allan Nordhøy Co-authored-by: Ananas Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Feroli Co-authored-by: Hosted Weblate Co-authored-by: Inside Co-authored-by: Jonas Kahnwald Co-authored-by: Kardi Demha Co-authored-by: Martijn Slob Co-authored-by: Michael Co-authored-by: Shafici Isxariifshe Co-authored-by: Skrripy Co-authored-by: SleepyOwl Co-authored-by: Walter H Co-authored-by: eightyy8 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translation: Cloudstream/App --- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-nl/strings.xml | 9 ++++--- app/src/main/res/values-no/strings.xml | 2 ++ app/src/main/res/values-pt/strings.xml | 15 +++++++++++ app/src/main/res/values-ro/strings.xml | 4 +++ app/src/main/res/values-so/strings.xml | 20 +++++++++++++++ app/src/main/res/values-tr/strings.xml | 35 ++++++++++++++++++-------- app/src/main/res/values-uk/strings.xml | 35 +++++++++++++------------- 9 files changed, 92 insertions(+), 30 deletions(-) create mode 100644 app/src/main/res/values-pt/strings.xml create mode 100644 app/src/main/res/values-so/strings.xml diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9148a7a3..e2717e06 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -487,4 +487,5 @@ PackageInstaller Aktualisierung gestartet Die Anwendung wird beim Beenden aktualisiert + Das Plugin wurde heruntergeladen \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 18d20be6..d6b48cd7 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -488,4 +488,5 @@ La aplicación se actualizará al salir Actualización iniciada Complemento descargado + Quitar de visto \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 0e69f9fc..60aa0bcf 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -143,8 +143,8 @@ Back-up gegevens Geladen back-up bestand Kan gegevens uit bestand niet herstellen %s - Gegevens succesvol opgeslagen - Opslagrechten ontbreken, probeer het opnieuw + De gegevens zijn opgeslagen + Opslagrechten ontbreken. Probeer het opnieuw. Fout bij het maken van een back-up %s Zoeken Accounts @@ -156,7 +156,7 @@ Verstuurt geen gegevens Toon filler episode voor anime Toon trailers - Toon posters van kitsu + Toon posters van Kitsu App-updates tonen Automatisch zoeken naar nieuwe updates bij het opstarten Update naar pre-releases @@ -399,4 +399,7 @@ Markeer als bekeken Geschiedenis Speel Trailer + Start de volgende episode wanneer deze afgelopen is + Volgende episode automatisch afspelen + De update is gestart \ No newline at end of file diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index e7ebc98f..f3807952 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -503,4 +503,6 @@ APK-installatør Noen enheter støtter ikke den nye pakkeinstallatøren. Prøv gammeldags alternativ hvis ting ikke installeres. Oppdatering startet + Programtillegg nedlastet + Programmet vil oppgraderes når du avslutter det \ No newline at end of file diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml new file mode 100644 index 00000000..e90f37a8 --- /dev/null +++ b/app/src/main/res/values-pt/strings.xml @@ -0,0 +1,15 @@ + + + %s Ep %d + %dh %dm + %dm + Episódio %d será lançado em + Poster + Capa do Episódio + \@string/result_poster_img_des + Capa Principal + Próximo Aleatório + Voltar + Trocar Provedor + %dd %dh %dm + \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 9087f8d9..239d144e 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -388,4 +388,8 @@ Trailer Istoric Marcare ca vizionat + Redă automat următorul episod + CloudStream + Vizionează trailerul + Actualizarea a început \ No newline at end of file diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml new file mode 100644 index 00000000..3d062df9 --- /dev/null +++ b/app/src/main/res/values-so/strings.xml @@ -0,0 +1,20 @@ + + + Metalaya: %s + %dm %ds %dd + %ds %dd + %dd + Xlq + Xalqadda %d waxa lasoo deyn doonaa + Daji + Dajintii ma guulaysan + Raadi + Tirtir + Qabo + la horay + La ekaysii + Ku buuxi + Soo dhawee + Dhammaan + Muusik + \ No newline at end of file diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 969acd70..2e3bddc6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -20,11 +20,11 @@ Poster @string/result_poster_img_des - Episode Poster - Main Poster - Next Random + Bölüm Posteri + Ana Poster + Sonraki Rastgele @string/play_episode - Go back + Geri git @string/home_change_provider_img_des Change Provider Preview Background @@ -386,10 +386,10 @@ Gölge Yükseltilmiş Alt yazı senkronu - 1000ms + 1000 ms Alt yazı gecikmesi - Alt yazılar %dms erken gözüküyorsa bunu kullanın - Alt yazılar %dms geç gözüküyorsa bunu kullanın + Alt yazılar %d ms erken gözüküyorsa bunu kullanın + Alt yazılar %d ms geç gözüküyorsa bunu kullanın Alt yazı gecikmesi yok - - - Poster - @string/result_poster_img_des - Episode Poster - Main Poster - Next Random - Go back - Change Provider - Preview Background - - - Velocidade (%.2fx) - Classificado: %.1f - Nova atualização encontrada!\n%s -> %s - Enchimento - - CloudStream - Reproduzir com CloudStream - Início - Pesquisa - Transferências - Opções - - Procurar… - Procurar em %s… - - Sem Dados - Mais Opções - Próximo episódio - Géneros - Partilhar - Abrir no Navegador - Saltar Carga - Carregando… - - Assistindo - Em Espera - Concluído - Abandonado - Planeio Assistir - Nenhuma - Assistindo de Novo - - Reproduzir Filme - Reproduzir Livestream - Transmitir Torrent - Fontes - Legendas - Voltar a tentar ligação… - Voltar Atrás - Reproduzir Episódio - - Transferir - Transferido - A Transferir - Transferência em Pausa - Transferência Iniciada - Transferência Falhou - Transferência Cancelada - Transferência Completa - Stream - - Erro a Carregar Links - Armazenamento Interno - - Dob - Leg - - Eliminar Ficheiro - Reproduzir Ficheiro - Retomar Transferência - Pausar Transferência - - Desativar relatório automático de erros - Mais info - Esconder - Reproduzir - Info - Filtrar Marcadores - Marcadores - Remover - Aplicar - Cancelar - Copiar - Fechar - Limpar - Guardar - - Velocidade de Reprodução - - Configurações de Legendas - Cor do Texto - Cor do Delineado - Cor do Fundo - Cor da Janela - Tipo de Borda - Elevação da Legenda - Fonte - Tamanho da Fonte - - Procurar usando fornecedores - Procurar usando tipos - - %d Benenes dadas aos devs - Nenhum Benene dada - - Auto-seleção de Idioma - Transferir Idiomas - Idioma da Legenda - Segure para retornar para o padrão - Importar fontes colocando em %s - Continuar a Assistir - - Remover - Mais info - - Uma VPN pode ser necessária para que este fornecedor funcione corretamente - Este fornecedor é um torrent, uma VPN é recomendada - - Metadados não são oferecidos pelo site, o carregamento do vídeo irá falhar se ele não existir no site. - - Descrição - Nenhum Enredo Encontrado - Nenhuma Descrição Encontrada - - Mostrar logcat 🐈 - - Picture-in-picture - Continua a reprodução num player em miniatura em cima de outras apps - Botão de redimensionamento do player - Remover as bordas negras - Legendas - Configurações de legendas do player - Legendas do Chromecast - Configurações de legendas do Chromecast - - Modo Eigengravy - Acrescenta uma opção de velocidade no player - Deslize para andar - Deslize para a esq. ou dir. para controlar o tempo no player - Deslize para mudar as configurações - Deslize do lado esq. ou dir. para ajustar brilho ou volume - -Reproduzir automaticamente próximo episódio - Começa o próximo episódio quando o atual termina - - Toque duplo para avançar - Toque duplo para pôr em pausa - Segundos avançados no player - Toque duplo no lado esq. ou dir. para andar para trás ou para a frente - Toque no meio para pôr em pausa - Usar brilho da sistema - Usar brilho do sistema no player em vez de uma sobreposição escura - - Atualizar progresso - Sincronizar automaticamente o progresso do seu episódio atual - - Restaurar dados a partir de backup - - Fazer backup - Arquivo de backup carregado - Falha ao restaurar dados do ficheiro %s - Dados guardados com sucesso - Permissões de armazenamento em falta, por favor tente de novo - Erro no backup de %s - - Procurar - Contas - Atualizações e backup - - Info - Procura Avançada - Mostra resultados separados por fornecedor - Só envia dados sobre falhas - Não envia nenhum dado - Mostrar episódios de enchimento para anime - Mostrar trailers - Mostrar posters do kitsu - - Esconder qualidades de vídeo selecionadas nos resultados da Pesquisa - Atualizações de plugin automáticas - - Mostrar atualizações da app - Procurar novas atualizações automaticamente ao iniciar - Atualizar para pré-lançamentos - Procura atualizações de pré-lançamento em vez de só lançamentos oficiais - Github - App light novel pelos mesmos devs - Junte-se ao Discord - Dar um benene aos devs - Benene dada - - Idioma da App - - Este fornecedor não tem suporte para Chromecast - Nenhum Link Encontrado - Link copiado para a área de transferência - Reproduzir episódio - Restaurar para o padrão - Desculpe, a aplicação falhou. Um relatório de erro anónimo será enviado para os - desenvolvedores - - - Temporada - Nenhuma Temporada - Episódio - Episódios - T - E - Nenhum Episódio encontrado - - Eliminar Ficheiro - Eliminar - Pôr em Pausa - Retomar - Isto apagará %s permanentemente\nTem a certeza? - %dm\nem falta - - Em Curso - Concluído - Estado - Ano - Classificação - Duração - Site - Sinopse - - Na fila - Sem Legendas - Padrão - - Livre - Usado - App - - - Filmes - Séries - Desenhos Animados - Anime - Torrents - Documentários - OVA - Dramas Asiáticos - Transmissões em Direto - NSFW - Outros - - - Filme - Série - Desenho Animado - Torrent - Documentário - Drama Asiático - Transmissão em Direto - NSFW - - Erro de fonte - Erro remoto - Erro de renderização - Erro inesperado do player - Erro de transferência, verifique permissões de armazenamento - - Episódio pelo Chromecast - Alternativa pelo Chromecast - Reproduzir na app - Reproduzir no %s - Reproduzir no navegador - Copiar link - Transferência Automática - Transferir por servidor alternativo - Recarregar links - Transferir legendas - - Etiqueta de qualidade - Etiqueta Dub - Etiqueta Sub - Título - Alternar elementos da interface no póster - - Nenhuma Atualização Encontrada - Procurar Atualização - - Fixar - Mudar Tamanho - Fonte - Saltar OP - - Não mostrar de novo - Saltar esta Atualização - Atualizar - Qualidade Preferida - Máximo de caracteres do título de vídeos - Resolução do player de vídeo - - Tamanho do buffer do vídeo - Comprimento do buffer do vídeo - Cache do vídeo em disco - Limpar cache de vídeo e imagem - - Causará travamentos aleatórios se definido muito alto. Não mude se tiver pouca memória RAM, como um Android TV ou um telefone antigo - Pode causar problemas em sistemas com pouco espaço de armazenamento se definido muito alto, como em dispositivos Android TV - - DNS sobre HTTPS - Útil para contornar bloqueios do fornecedor de internet - - Clonar site - Remover site - Adiciona um clone de um site existente, com um url diferente - - Caminho de transferência - - Url do servidor Nginx - - Mostrar Anime Dobrado/Legendado - - Ajustar para a Tela - Esticar - Aumentar - - Aviso Legal - Quaisquer questões legais relativas ao conteúdo desta aplicação - devem ser levados em conta com os próprios anfitriões e fornecedores de arquivos, pois não somos afiliados a eles. - - Em caso de violação de direitos autorais, favor contatar diretamente as partes responsáveis ou os sites de streaming. - - O aplicativo é puramente para uso educacional e pessoal. - - O CloudStream 3 não hospeda nenhum conteúdo no aplicativo, e não tem controle sobre qual mídia é colocada ou retirada. - O CloudStream 3 funciona como qualquer outro mecanismo de busca, como o Google. O CloudStream 3 não hospeda, não faz upload ou - gerenciar qualquer vídeo, filme ou conteúdo. Ele simplesmente rasteja, agrega e exibe links em um conveniente, - interface amigável para o usuário. - - Ela apenas raspa sites de terceiros que podem ser acessados publicamente através de qualquer navegador web normal. É o - responsabilidade do usuário de evitar qualquer ação que possa violar as leis que regem sua localidade. Utilizar - CloudStream 3 por sua própria conta e risco. - - Geral - Botão Aleatório - Mostra o botão Aleatório na página inicial - Idioma dos fornecedores - Layout da App - Mídia preferida -Ativar NSFW em fornecedores compatíveis -Fornecedores - Codificação das legendas - Layout - - Auto - Layout de TV - Layout de telemóvel - Layout de emulador - - Cor Primária - Tema do App - Local do título do poster - Coloca o título debaixo do poster - - - senha123 - MeuNomeFixe - ola@mundo.com - 127.0.0.1 - MeuSiteFixe - examplo.com - Codigo da Língua (pt) - - Conta - Sair - Entrar - Mudar de conta - Adicionar conta - Criar conta - Adicionar sincronização - %s adicionado - Sincronizar - Nota - %d / 10 - /?? - /%d - %s autenticado - Falha em autenticar para %s - - - Nenhuma - Normal - Tudo - Max - Min - Contornado - Deprimido - Sombreado - Em Relevo - Sincronizar legendas - Atraso de legenda - Use isto se as legendas forem mostradas %dms adiantadas - Use isto se as legendas forem mostradas %dms atrasadas - Sem atraso de legenda - - Luís argüia à Júlia que «brações, fé, chá, óxido, pôr, zângão» eram palavras do português - - Recomendada - %s carregada - Carregar de arquivo - Carregar da Internet - Arquivo baixado - Protagonista - Coadjuvante - Figurante - - Fonte - Aleatório - - Em breve… - - Imagem de Poster - Player - Resolução e título - Título - Resolução - Id inválida - Dado inválido - -URL inválido - Erro - Remover legendas ocultas(CC) das legendas - Remover bloat das legendas - -Filtrar por linguagem preferida - Extras - Trailer - - Próximo - Ver vídeos nestas linguagens - Anterior - Saltar setup - - Change the look of the app to suit your device - Crash reporting - What do you want to see - Feito - Extensões - Adicionar repositório - Nome do repositório - URL do repositório - Plugin Carregado - Plugin Apagado - Falha ao carregar %s - - Iniciada a transferência %d %s - Transferido %d %s com sucesso - Tudo %s já transferido - Transferência em batch - plugin - plugins - Isto irá apagar todos os repositórios de plugins - Apagar repositório - Transferir lista de sites a usar - Transferido: %d - Desativado: %d - Não transferido: %d - Adicionar um repositório para instalar extensões de sites - Ver repositórios da comunidade - Lista pública - Todas as legendas em maiúsculas - - Transferir todos os plugins deste repositório? - %s (Desativado) - From b2b894caa9cf50fbe84fe868a572b202e1ac37b4 Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Sat, 21 Jan 2023 13:11:40 +0100 Subject: [PATCH 06/47] Translated using Weblate (Somali) Currently translated at 17.9% (102 of 567 strings) Translated using Weblate (Polish) Currently translated at 100.0% (567 of 567 strings) Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translation: Cloudstream/App --- app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-so/strings.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index c1999697..ed079c58 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -488,4 +488,5 @@ Aplikacja zostanie zaktualizowana po wyjściu Aktualizacja rozpoczęta Wtyczka pobrana + Usuń z obejrzanych \ No newline at end of file diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 3d062df9..dd3aa432 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -4,7 +4,7 @@ %dm %ds %dd %ds %dd %dd - Xlq + %s Ep %d Xalqadda %d waxa lasoo deyn doonaa Daji Dajintii ma guulaysan From 65fda1889ce2e1f53eb3fbfa6f7e68b5331a5c43 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Sat, 21 Jan 2023 20:05:37 +0100 Subject: [PATCH 07/47] expandable resume watching go brrr --- .../ui/home/HomeParentItemAdapter.kt | 10 +- .../ui/home/HomeParentItemAdapterPreview.kt | 45 ++++- .../ui/result/ResultViewModel2.kt | 15 +- .../main/res/layout/fragment_home_head.xml | 154 ++++++++++-------- app/src/main/res/layout/homepage_parent.xml | 33 ++-- .../main/res/layout/homepage_parent_tv.xml | 2 +- 6 files changed, 155 insertions(+), 104 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt index 8b7832e2..e6999c9e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapter.kt @@ -184,9 +184,8 @@ open class ParentItemAdapter( private val expandCallback: ((String) -> Unit)? = null, ) : RecyclerView.ViewHolder(itemView) { - val title: TextView = itemView.home_parent_item_title + val title: TextView = itemView.home_child_more_info val recyclerView: RecyclerView = itemView.home_child_recyclerview - private val moreInfo: FrameLayout? = itemView.home_child_more_info fun update(expand: HomeViewModel.ExpandableHomepageList) { val info = expand.list @@ -248,9 +247,10 @@ open class ParentItemAdapter( }) //(recyclerView.adapter as HomeChildItemAdapter).notifyDataSetChanged() - - moreInfo?.setOnClickListener { - moreInfoClickCallback.invoke(expand) + if (!isTvSettings()) { + title.setOnClickListener { + moreInfoClickCallback.invoke(expand) + } } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt index 7564a9de..94a1a526 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeParentItemAdapterPreview.kt @@ -14,6 +14,7 @@ import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity +import com.lagradost.cloudstream3.HomePageList import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchResponse @@ -31,9 +32,10 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectSt import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView import com.lagradost.cloudstream3.utils.UIHelper.setImage -import kotlinx.android.synthetic.main.activity_main_tv.view.* +import kotlinx.android.synthetic.main.activity_main.view.* import kotlinx.android.synthetic.main.fragment_home_head.view.* import kotlinx.android.synthetic.main.fragment_home_head.view.home_bookmarked_child_recyclerview +import kotlinx.android.synthetic.main.fragment_home_head.view.home_watch_parent_item_title import kotlinx.android.synthetic.main.fragment_home_head_tv.view.* import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_bookmarked_holder import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_none_padding @@ -46,11 +48,12 @@ import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_on_ho import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_type_watching_btt import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_child_recyclerview import kotlinx.android.synthetic.main.fragment_home_head_tv.view.home_watch_holder +import kotlinx.android.synthetic.main.toast.view.* class HomeParentItemAdapterPreview( items: MutableList, val clickCallback: (SearchClickCallback) -> Unit, - moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, + private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit, expandCallback: ((String) -> Unit)? = null, private val loadCallback: (LoadClickCallback) -> Unit, private val loadMoreCallback: (() -> Unit), @@ -136,7 +139,8 @@ class HomeParentItemAdapterPreview( clickCallback, reloadStored, loadStoredData, - searchQueryCallback + searchQueryCallback, + moreInfoClickCallback ).also { this.holder = it } @@ -182,7 +186,8 @@ class HomeParentItemAdapterPreview( private val searchClickCallback: (SearchClickCallback) -> Unit, private val reloadStored: () -> Unit, private val loadStoredData: ((Set) -> Unit), - private val searchQueryCallback: ((Pair) -> Unit) + private val searchQueryCallback: ((Pair) -> Unit), + private val moreInfoClickCallback: (HomeViewModel.ExpandableHomepageList) -> Unit ) : RecyclerView.ViewHolder(itemView) { private var previewAdapter: HomeScrollAdapter? = null private val previewViewpager: ViewPager2? = itemView.home_preview_viewpager @@ -602,11 +607,43 @@ class HomeParentItemAdapterPreview( fun updateResume(resumeWatching: List) { resumeHolder?.isVisible = resumeWatching.isNotEmpty() resumeAdapter?.updateList(resumeWatching) + + if (!isTvSettings()) { + itemView.home_watch_parent_item_title?.setOnClickListener { + moreInfoClickCallback.invoke( + HomeViewModel.ExpandableHomepageList( + HomePageList( + itemView.home_watch_parent_item_title?.text.toString(), + resumeWatching, + false + ), 1, false + ) + ) + } + } } fun updateBookmarks(data: Pair>) { bookmarkHolder?.isVisible = data.first bookmarkAdapter?.updateList(data.second) + if (!isTvSettings()) { + itemView.home_bookmark_parent_item_title?.setOnClickListener { + val items = toggleList.mapNotNull { it.first }.filter { it.isChecked } + if (items.isEmpty()) return@setOnClickListener // we don't want to show an empty dialog + val textSum = items + .mapNotNull { it.text }.joinToString() + + moreInfoClickCallback.invoke( + HomeViewModel.ExpandableHomepageList( + HomePageList( + textSum, + data.second, + false + ), 1, false + ) + ) + } + } } fun setAvailableWatchStatusTypes(availableWatchStatusTypes: Pair, Set>) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 4bc7bc48..6e249aa4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -421,7 +421,6 @@ class ResultViewModel2 : ViewModel() { fun updateWatchStatus(currentResponse: LoadResponse, status: WatchType) { val currentId = currentResponse.getId() - val resultPage = currentResponse DataStoreHelper.setResultWatchState(currentId, status.internalId) val current = DataStoreHelper.getBookmarkedData(currentId) @@ -432,12 +431,12 @@ class ResultViewModel2 : ViewModel() { currentId, current?.bookmarkedTime ?: currentTime, currentTime, - resultPage.name, - resultPage.url, - resultPage.apiName, - resultPage.type, - resultPage.posterUrl, - resultPage.year + currentResponse.name, + currentResponse.url, + currentResponse.apiName, + currentResponse.type, + currentResponse.posterUrl, + currentResponse.year ) ) } @@ -1628,7 +1627,7 @@ class ResultViewModel2 : ViewModel() { if (ranges?.contains(range) != true) { // if the current ranges does not include the range then select the range with the closest matching start episode // this usually happends when dub has less episodes then sub -> the range does not exist - ranges?.minByOrNull { abs(it.startEpisode - range.startEpisode) }?.let { r -> + ranges?.minByOrNull { kotlin.math.abs(it.startEpisode - range.startEpisode) }?.let { r -> postEpisodeRange(indexer, r) return } diff --git a/app/src/main/res/layout/fragment_home_head.xml b/app/src/main/res/layout/fragment_home_head.xml index 28d7ca2f..da3a3bbd 100644 --- a/app/src/main/res/layout/fragment_home_head.xml +++ b/app/src/main/res/layout/fragment_home_head.xml @@ -1,13 +1,13 @@ - + android:orientation="vertical"> + android:layout_height="500dp" + tools:visibility="gone"> @@ -80,7 +82,6 @@ --> + android:text="@string/continue_watching" + app:drawableRightCompat="@drawable/ic_baseline_arrow_forward_24" + app:drawableTint="?attr/white" + android:background="?android:attr/selectableItemBackground" + android:contentDescription="@string/home_more_info"/> - - android:foreground="?android:attr/selectableItemBackgroundBorderless" - android:nextFocusLeft="@id/nav_rail_view" - android:nextFocusUp="@id/home_watch_child_recyclerview" - android:nextFocusForward="@id/home_bookmarked_child_recyclerview" - android:paddingStart="12dp" - android:paddingTop="5dp" - android:paddingEnd="12dp" - - android:paddingBottom="5dp" - android:requiresFadingEdge="horizontal"> - - + android:layout_marginEnd="50dp" + android:fadingEdge="horizontal" - + + - android:nextFocusLeft="@id/nav_rail_view" - android:nextFocusRight="@id/home_plan_to_watch_btt" - android:text="@string/type_watching" /> + - android:nextFocusLeft="@id/home_type_watching_btt" - android:nextFocusRight="@id/home_type_on_hold_btt" - android:text="@string/type_plan_to_watch" /> + - android:nextFocusLeft="@id/home_plan_to_watch_btt" - android:nextFocusRight="@id/home_type_dropped_btt" - android:text="@string/type_on_hold" /> + - android:nextFocusLeft="@id/home_type_on_hold_btt" - android:nextFocusRight="@id/home_type_completed_btt" - android:text="@string/type_dropped" /> + - android:layout_height="wrap_content" - android:nextFocusLeft="@id/home_type_dropped_btt" - android:text="@string/type_completed" /> - - + + + + + + + - - - - - - + Date: Sat, 21 Jan 2023 23:22:48 +0100 Subject: [PATCH 08/47] very nice long hold popups --- .../lagradost/cloudstream3/MainActivity.kt | 138 +++++++++++- .../cloudstream3/ui/home/HomeFragment.kt | 12 ++ .../cloudstream3/ui/result/ResultFragment.kt | 1 + .../ui/result/ResultViewModel2.kt | 27 ++- .../cloudstream3/ui/search/SearchFragment.kt | 2 + .../cloudstream3/ui/search/SearchHelper.kt | 12 +- .../res/layout/bottom_resultview_preview.xml | 197 ++++++++++++++++++ app/src/main/res/layout/loading_list.xml | 44 ++-- 8 files changed, 392 insertions(+), 41 deletions(-) create mode 100644 app/src/main/res/layout/bottom_resultview_preview.xml diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 4907411f..857eaa6a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1,16 +1,14 @@ package com.lagradost.cloudstream3 import android.content.ComponentName -import android.content.DialogInterface +import android.content.Context import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration import android.os.Bundle +import android.util.AttributeSet import android.util.Log -import android.view.KeyEvent -import android.view.Menu -import android.view.MenuItem -import android.view.WindowManager +import android.view.* import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IdRes @@ -19,6 +17,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy @@ -31,6 +30,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.google.android.gms.cast.framework.* +import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.navigationrail.NavigationRailView import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.lagradost.cloudstream3.APIHolder.allProviders @@ -46,8 +46,7 @@ import com.lagradost.cloudstream3.CommonActivity.onDialogDismissedEvent import com.lagradost.cloudstream3.CommonActivity.onUserLeaveHint import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.CommonActivity.updateLocale -import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.network.initClient import com.lagradost.cloudstream3.plugins.PluginManager import com.lagradost.cloudstream3.plugins.PluginManager.loadAllOnlinePlugins @@ -61,9 +60,13 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStri import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.appStringSearch import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.inAppAuths import com.lagradost.cloudstream3.ui.APIRepository +import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.home.HomeViewModel +import com.lagradost.cloudstream3.ui.result.ResultViewModel2 import com.lagradost.cloudstream3.ui.result.START_ACTION_RESUME_LATEST +import com.lagradost.cloudstream3.ui.result.setImage +import com.lagradost.cloudstream3.ui.result.setText import com.lagradost.cloudstream3.ui.search.SearchFragment import com.lagradost.cloudstream3.ui.search.SearchResultBuilder import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings @@ -74,6 +77,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.html import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadRepository @@ -86,9 +90,11 @@ import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.checkWrite import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate @@ -96,6 +102,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.bottom_resultview_preview.* import kotlinx.android.synthetic.main.fragment_result_swipe.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -244,6 +251,10 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { Event() // homepage api, used to speed up time to load for homepage val afterRepositoryLoadedEvent = Event() + // kinda shitty solution, but cant com main->home otherwise for popups + val bookmarksUpdatedEvent = Event() + + /** * @return true if the str has launched an app task (be it successful or not) * @param isWebview does not handle providers and opening download page if true. Can still add repos and login. @@ -336,6 +347,16 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } + var lastPopup : SearchResponse? = null + fun loadPopup(result: SearchResponse) { + lastPopup = result + viewModel.load( + this, result.url, result.apiName, false, if (getApiDubstatusSettings() + .contains(DubStatus.Dubbed) + ) DubStatus.Dubbed else DubStatus.Subbed, null + ) + } + override fun onColorSelected(dialogId: Int, color: Int) { onColorSelectedEvent.invoke(Pair(dialogId, color)) } @@ -619,6 +640,37 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } + lateinit var viewModel: ResultViewModel2 + + override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { + viewModel = + ViewModelProvider(this)[ResultViewModel2::class.java] + + return super.onCreateView(name, context, attrs) + } + + private fun hidePreviewPopupDialog() { + viewModel.clear() + bottomPreviewPopup.dismissSafe(this) + } + + var bottomPreviewPopup: BottomSheetDialog? = null + private fun showPreviewPopupDialog(): BottomSheetDialog { + val ret = (bottomPreviewPopup ?: run { + val builder = + BottomSheetDialog(this) + builder.setContentView(R.layout.bottom_resultview_preview) + builder.setOnDismissListener { + bottomPreviewPopup = null + viewModel.clear() + } + builder.setCanceledOnTouchOutside(true) + builder.show() + builder + }) + bottomPreviewPopup = ret + return ret + } override fun onCreate(savedInstanceState: Bundle?) { app.initClient(this) @@ -708,6 +760,78 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { builder.show().setDefaultFocus() } + observeNullable(viewModel.page) { resource -> + if (resource == null) { + bottomPreviewPopup.dismissSafe(this) + return@observeNullable + } + when (resource) { + is Resource.Failure -> { + showToast(this, R.string.error) + hidePreviewPopupDialog() + } + is Resource.Loading -> { + showPreviewPopupDialog().apply { + resultview_preview_loading?.isVisible = true + resultview_preview_result?.isVisible = false + resultview_preview_loading_shimmer?.startShimmer() + } + } + is Resource.Success -> { + val d = resource.value + showPreviewPopupDialog().apply { + resultview_preview_loading?.isVisible = false + resultview_preview_result?.isVisible = true + resultview_preview_loading_shimmer?.stopShimmer() + + resultview_preview_title?.text = d.title + + resultview_preview_meta_type.setText(d.typeText) + resultview_preview_meta_year.setText(d.yearText) + resultview_preview_meta_duration.setText(d.durationText) + resultview_preview_meta_rating.setText(d.ratingText) + + resultview_preview_description?.setText(d.plotText) + resultview_preview_poster?.setImage( + d.posterImage ?: d.posterBackgroundImage + ) + + resultview_preview_poster?.setOnClickListener { + //viewModel.updateWatchStatus(WatchType.PLANTOWATCH) + val value = viewModel.watchStatus.value ?: WatchType.NONE + + this@MainActivity.showBottomDialog( + WatchType.values().map { getString(it.stringRes) }.toList(), + value.ordinal, + this@MainActivity.getString(R.string.action_add_to_bookmarks), + showApply = false, + {}) { + viewModel.updateWatchStatus(WatchType.values()[it]) + bookmarksUpdatedEvent(true) + } + } + + if (!isTvSettings()) // dont want this clickable on tv layout + resultview_preview_description?.setOnClickListener { view -> + view.context?.let { ctx -> + val builder: AlertDialog.Builder = + AlertDialog.Builder(ctx, R.style.AlertDialogCustom) + builder.setMessage(d.plotText.asString(ctx).html()) + .setTitle(d.plotHeaderText.asString(ctx)) + .show() + } + } + + resultview_preview_more_info?.setOnClickListener { + hidePreviewPopupDialog() + lastPopup?.let { + loadSearchResult(it) + } + } + } + } + } + } // ioSafe { // val plugins = diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index bb183f12..8a8f90b4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -32,6 +32,7 @@ import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent +import com.lagradost.cloudstream3.MainActivity.Companion.bookmarksUpdatedEvent import com.lagradost.cloudstream3.MainActivity.Companion.mainPluginsLoadedEvent import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.logError @@ -435,6 +436,11 @@ class HomeFragment : Fragment() { return inflater.inflate(layout, container, false) } + override fun onDestroyView() { + bottomSheetDialog?.ownHide() + super.onDestroyView() + } + private fun fixGrid() { activity?.getSpanCount()?.let { currentSpan = it @@ -461,14 +467,20 @@ class HomeFragment : Fragment() { fixGrid() } + fun bookmarksUpdated(_data : Boolean) { + reloadStored() + } + override fun onResume() { super.onResume() reloadStored() + bookmarksUpdatedEvent += ::bookmarksUpdated afterPluginsLoadedEvent += ::afterPluginsLoaded mainPluginsLoadedEvent += ::afterMainPluginsLoaded } override fun onStop() { + bookmarksUpdatedEvent -= ::bookmarksUpdated afterPluginsLoadedEvent -= ::afterPluginsLoaded mainPluginsLoadedEvent -= ::afterMainPluginsLoaded super.onStop() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 68a57b7f..9cfbf45c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -850,6 +850,7 @@ open class ResultFragment : ResultTrailerPlayer() { } observe(viewModel.page) { data -> + if(data == null) return@observe when (data) { is Resource.Success -> { val d = data.value diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 6e249aa4..6ed32b15 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -55,7 +55,6 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setResultSeason import com.lagradost.cloudstream3.utils.UIHelper.navigate import kotlinx.coroutines.* import java.io.File -import java.lang.Math.abs import java.util.concurrent.TimeUnit @@ -314,6 +313,11 @@ data class ExtractedTrailerData( class ResultViewModel2 : ViewModel() { private var currentResponse: LoadResponse? = null + fun clear() { + currentResponse = null + _page.postValue(null) + } + data class EpisodeIndexer( val dubStatus: DubStatus, val season: Int, @@ -340,9 +344,9 @@ class ResultViewModel2 : ViewModel() { //private val currentHeaderName get() = currentResponse?.name - private val _page: MutableLiveData> = - MutableLiveData(Resource.Loading()) - val page: LiveData> = _page + private val _page: MutableLiveData?> = + MutableLiveData(null) + val page: LiveData?> = _page private val _episodes: MutableLiveData>> = MutableLiveData(ResourceSome.Loading()) @@ -398,7 +402,6 @@ class ResultViewModel2 : ViewModel() { private val _selectedDubStatusIndex: MutableLiveData = MutableLiveData(-1) val selectedDubStatusIndex: LiveData = _selectedDubStatusIndex - private val _loadedLinks: MutableLiveData> = MutableLiveData(Some.None) val loadedLinks: LiveData> = _loadedLinks @@ -1627,10 +1630,11 @@ class ResultViewModel2 : ViewModel() { if (ranges?.contains(range) != true) { // if the current ranges does not include the range then select the range with the closest matching start episode // this usually happends when dub has less episodes then sub -> the range does not exist - ranges?.minByOrNull { kotlin.math.abs(it.startEpisode - range.startEpisode) }?.let { r -> - postEpisodeRange(indexer, r) - return - } + ranges?.minByOrNull { kotlin.math.abs(it.startEpisode - range.startEpisode) } + ?.let { r -> + postEpisodeRange(indexer, r) + return + } } val isMovie = currentResponse?.isMovie() == true @@ -2111,6 +2115,7 @@ class ResultViewModel2 : ViewModel() { showFillers: Boolean, dubStatus: DubStatus, autostart: AutoResume?, + loadTrailers: Boolean = true, ) = viewModelScope.launchSafe { _page.postValue(Resource.Loading(url)) @@ -2189,8 +2194,8 @@ class ResultViewModel2 : ViewModel() { System.currentTimeMillis(), ) ) - - loadTrailers(data.value) + if (loadTrailers) + loadTrailers(data.value) postSuccessful( data.value, updateEpisodes = true, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 1da30691..4144a042 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -45,6 +45,7 @@ import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.updateChips import com.lagradost.cloudstream3.ui.home.ParentItemAdapter import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.utils.AppUtils.ownHide import com.lagradost.cloudstream3.utils.AppUtils.ownShow import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.Coroutines.main @@ -121,6 +122,7 @@ class SearchFragment : Fragment() { override fun onDestroyView() { hideKeyboard() + bottomSheetDialog?.ownHide() super.onDestroyView() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt index 1de89809..45336d5b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchHelper.kt @@ -3,11 +3,13 @@ package com.lagradost.cloudstream3.ui.search import android.app.Activity import android.widget.Toast import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.MainActivity import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_PLAY_FILE import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.ui.result.START_ACTION_LOAD_EP +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.VideoDownloadHelper @@ -54,7 +56,15 @@ object SearchHelper { } } SEARCH_ACTION_SHOW_METADATA -> { - showToast(activity, callback.card.name, Toast.LENGTH_SHORT) + if(!isTvSettings()) { // we only want this on phone as UI is not done yet on tv + (activity as? MainActivity?)?.apply { + loadPopup(callback.card) + } ?: kotlin.run { + showToast(activity, callback.card.name, Toast.LENGTH_SHORT) + } + } else { + showToast(activity, callback.card.name, Toast.LENGTH_SHORT) + } } } } diff --git a/app/src/main/res/layout/bottom_resultview_preview.xml b/app/src/main/res/layout/bottom_resultview_preview.xml new file mode 100644 index 00000000..ce41cb65 --- /dev/null +++ b/app/src/main/res/layout/bottom_resultview_preview.xml @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/loading_list.xml b/app/src/main/res/layout/loading_list.xml index ccd4a8d6..1ed01c8e 100644 --- a/app/src/main/res/layout/loading_list.xml +++ b/app/src/main/res/layout/loading_list.xml @@ -1,59 +1,59 @@ + android:layout_width="match_parent" + android:layout_height="200dp" + android:orientation="vertical" + android:paddingTop="@dimen/loading_margin" + android:paddingBottom="@dimen/loading_margin"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="match_parent" /> + android:layout_width="@dimen/loading_margin" + android:layout_height="wrap_content" /> From 0b4de8181159e31ded92a90c8f91bc20a745564e Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Mon, 23 Jan 2023 00:29:14 +0100 Subject: [PATCH 09/47] (hopefully) Fix home search and OpenSubtitles --- .../syncproviders/providers/OpenSubtitlesApi.kt | 4 ++-- app/src/main/res/layout/fragment_home_head.xml | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt index 8f06c98e..3e372c2d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/OpenSubtitlesApi.kt @@ -166,7 +166,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi val fixedLang = fixLanguage(query.lang) val imdbId = query.imdb ?: 0 - val queryText = query.query.replace(" ", "+") + val queryText = query.query val epNum = query.epNumber ?: 0 val seasonNum = query.seasonNumber ?: 0 val yearNum = query.year ?: 0 @@ -177,7 +177,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi val searchQueryUrl = when (imdbId > 0) { //Use imdb_id to search if its valid true -> "$host/subtitles?imdb_id=$imdbId&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" - false -> "$host/subtitles?query=${URLEncoder.encode(queryText.lowercase(), StandardCharsets.UTF_8.toString())}&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" + false -> "$host/subtitles?query=${queryText}&languages=${fixedLang}$yearQuery$epQuery$seasonQuery" } val req = app.get( diff --git a/app/src/main/res/layout/fragment_home_head.xml b/app/src/main/res/layout/fragment_home_head.xml index da3a3bbd..0ee50042 100644 --- a/app/src/main/res/layout/fragment_home_head.xml +++ b/app/src/main/res/layout/fragment_home_head.xml @@ -16,10 +16,8 @@ + android:layout_height="500dp"> From de720983a6065b37e7d7a42b5d1b35289648177f Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 24 Jan 2023 19:15:33 +0100 Subject: [PATCH 10/47] Translated using Weblate (Russian) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 83.7% (475 of 567 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Portuguese) Currently translated at 81.3% (461 of 567 strings) Translated using Weblate (Somali) Currently translated at 99.6% (565 of 567 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 46.7% (265 of 567 strings) Translated using Weblate (Norwegian Nynorsk) Currently translated at 46.7% (265 of 567 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Esperanto) Currently translated at 28.7% (163 of 567 strings) Translated using Weblate (Persian) Currently translated at 19.0% (108 of 567 strings) Translated using Weblate (Hungarian) Currently translated at 58.7% (333 of 567 strings) Translated using Weblate (German) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Russian) Currently translated at 83.5% (474 of 567 strings) Translated using Weblate (Russian) Currently translated at 83.5% (474 of 567 strings) Translated using Weblate (Kannada) Currently translated at 14.9% (85 of 567 strings) Translated using Weblate (Urdu) Currently translated at 76.3% (433 of 567 strings) Translated using Weblate (Tamil) Currently translated at 18.8% (107 of 567 strings) Translated using Weblate (Hebrew) Currently translated at 37.5% (213 of 567 strings) Translated using Weblate (Bengali) Currently translated at 40.7% (231 of 567 strings) Translated using Weblate (Bengali) Currently translated at 40.7% (231 of 567 strings) Translated using Weblate (Bengali) Currently translated at 40.7% (231 of 567 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Chinese (Traditional)) Currently translated at 99.2% (563 of 567 strings) Translated using Weblate (Vietnamese) Currently translated at 87.6% (497 of 567 strings) Translated using Weblate (Vietnamese) Currently translated at 87.6% (497 of 567 strings) Translated using Weblate (Vietnamese) Currently translated at 87.6% (497 of 567 strings) Translated using Weblate (Turkish) Currently translated at 97.0% (550 of 567 strings) Translated using Weblate (Tagalog) Currently translated at 59.2% (336 of 567 strings) Translated using Weblate (Tagalog) Currently translated at 59.2% (336 of 567 strings) Translated using Weblate (Tagalog) Currently translated at 59.2% (336 of 567 strings) Translated using Weblate (Swedish) Currently translated at 79.1% (449 of 567 strings) Translated using Weblate (Swedish) Currently translated at 79.1% (449 of 567 strings) Translated using Weblate (Swedish) Currently translated at 79.1% (449 of 567 strings) Translated using Weblate (Romanian) Currently translated at 74.7% (424 of 567 strings) Translated using Weblate (Romanian) Currently translated at 74.7% (424 of 567 strings) Translated using Weblate (Romanian) Currently translated at 74.7% (424 of 567 strings) Translated using Weblate (Polish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Norwegian Bokmål) Currently translated at 93.4% (530 of 567 strings) Translated using Weblate (Dutch) Currently translated at 79.3% (450 of 567 strings) Translated using Weblate (Dutch) Currently translated at 79.3% (450 of 567 strings) Translated using Weblate (Dutch) Currently translated at 79.3% (450 of 567 strings) Translated using Weblate (Malayalam) Currently translated at 38.9% (221 of 567 strings) Translated using Weblate (Malayalam) Currently translated at 38.9% (221 of 567 strings) Translated using Weblate (Malayalam) Currently translated at 38.9% (221 of 567 strings) Translated using Weblate (Macedonian) Currently translated at 51.1% (290 of 567 strings) Translated using Weblate (Macedonian) Currently translated at 51.1% (290 of 567 strings) Translated using Weblate (Macedonian) Currently translated at 51.1% (290 of 567 strings) Translated using Weblate (Italian) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Indonesian) Currently translated at 99.6% (565 of 567 strings) Translated using Weblate (Croatian) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Hindi) Currently translated at 39.5% (224 of 567 strings) Translated using Weblate (Hindi) Currently translated at 39.5% (224 of 567 strings) Translated using Weblate (Hindi) Currently translated at 39.5% (224 of 567 strings) Translated using Weblate (French) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Greek) Currently translated at 99.8% (566 of 567 strings) Translated using Weblate (Czech) Currently translated at 72.6% (412 of 567 strings) Translated using Weblate (Czech) Currently translated at 72.6% (412 of 567 strings) Translated using Weblate (Czech) Currently translated at 72.6% (412 of 567 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 81.8% (464 of 567 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 81.8% (464 of 567 strings) Translated using Weblate (bp (generated) (bp)) Currently translated at 81.8% (464 of 567 strings) Translated using Weblate (Bulgarian) Currently translated at 99.8% (566 of 567 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Russian) Currently translated at 83.4% (473 of 567 strings) Translated using Weblate (Russian) Currently translated at 83.2% (472 of 567 strings) Translated using Weblate (Polish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Russian) Currently translated at 81.6% (463 of 567 strings) Translated using Weblate (Russian) Currently translated at 81.6% (463 of 567 strings) Translated using Weblate (Polish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Indonesian) Currently translated at 99.6% (565 of 567 strings) Translated using Weblate (Somali) Currently translated at 99.6% (565 of 567 strings) Translated using Weblate (Russian) Currently translated at 60.4% (343 of 567 strings) Translated using Weblate (Vietnamese) Currently translated at 87.6% (497 of 567 strings) Translated using Weblate (Russian) Currently translated at 55.9% (317 of 567 strings) Translated using Weblate (Russian) Currently translated at 55.9% (317 of 567 strings) Translated using Weblate (Russian) Currently translated at 55.9% (317 of 567 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Russian) Currently translated at 53.2% (302 of 567 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Turkish) Currently translated at 97.0% (550 of 567 strings) Translated using Weblate (Somali) Currently translated at 69.6% (395 of 567 strings) Translated using Weblate (Italian) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (567 of 567 strings) new string translations Co-authored-by: Alexey Co-authored-by: Allan Nordhøy Co-authored-by: Anonymous Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Dan Co-authored-by: Fikri Akbar Co-authored-by: Hosted Weblate Co-authored-by: Jonas Kahnwald Co-authored-by: Piotr Z Co-authored-by: Rex_sa Co-authored-by: Sandyran Co-authored-by: Sdarfeesh Co-authored-by: Shafici Isxariifshe Co-authored-by: Sina Sharifkazemi Co-authored-by: SleepyOwl Co-authored-by: Translator-3000 Co-authored-by: Walter H Co-authored-by: eightyy8 Co-authored-by: kaajjo Co-authored-by: tuan041 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bp/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/cs/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/eo/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fa/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/fr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/he/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/hu/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/kn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/mk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ml/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nb_NO/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/nn/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pt/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sv/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ta/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ur/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hant/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 34 +- app/src/main/res/values-bg/strings.xml | 25 +- app/src/main/res/values-bp/strings.xml | 42 +- app/src/main/res/values-cs/strings.xml | 47 +- app/src/main/res/values-el/strings.xml | 3 +- app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-fa/strings.xml | 4 + app/src/main/res/values-fr/strings.xml | 9 +- app/src/main/res/values-hi/strings.xml | 16 +- app/src/main/res/values-hr/strings.xml | 48 +-- app/src/main/res/values-in/strings.xml | 48 +-- app/src/main/res/values-it/strings.xml | 18 +- app/src/main/res/values-kn/strings.xml | 3 +- app/src/main/res/values-mk/strings.xml | 6 +- app/src/main/res/values-ml/strings.xml | 14 +- app/src/main/res/values-nl/strings.xml | 25 +- app/src/main/res/values-no/strings.xml | 25 +- app/src/main/res/values-pl/strings.xml | 175 ++++---- app/src/main/res/values-ro/strings.xml | 32 +- app/src/main/res/values-ru/strings.xml | 248 ++++++++++- app/src/main/res/values-so/strings.xml | 480 ++++++++++++++++++++- app/src/main/res/values-sv/strings.xml | 10 +- app/src/main/res/values-tl/strings.xml | 8 +- app/src/main/res/values-tr/strings.xml | 59 ++- app/src/main/res/values-uk/strings.xml | 4 +- app/src/main/res/values-vi/strings.xml | 34 +- app/src/main/res/values-zh-rTW/strings.xml | 46 +- app/src/main/res/values-zh/strings.xml | 53 +-- 28 files changed, 1047 insertions(+), 471 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index ecb15f78..3a0c97e7 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -102,7 +102,7 @@ حذف مزيد من المعلومات قد تكون هناك حاجة إلى VPN لكي يعمل هذا المزود بشكل صحيح - هذا المزود هو تورنت ، يوصى باستخدام شبكة ظاهرية خاصة + هذا المزود هو تورنت ، يوصى باستخدام شبكة ظاهرية خاصة لا يتم توفير البيانات الوصفية بواسطة الموقع ، وسيفشل تحميل الفيديو إذا لم يكن موجودًا في الموقع. الوصف لم يتم العثور على وصف @@ -184,8 +184,10 @@ إستئناف -٣٠ +٣٠ - سوف يتم الحذف نهائيا %s\nهل أنت متأكد? - %dm\nمتبقية + سوف يتم الحذف نهائيا %s +\nهل أنت متأكد\? + %dm +\nمتبقية جاري التنفيذ اكتمل الحالة @@ -233,7 +235,7 @@ مرآة كروم كاست تشغيل في التطبيق %s تشغيل في - تشغيل في الويب + تشغيل في الويب نسخ الرابط التحميل التلقائي تحميل بجودات مختلفة @@ -279,22 +281,7 @@ تكبير إخلاء مسؤولية legal_notice_key - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. عام زر العشوائي إظهار زر العشوائي على الصفحة الرئيسية @@ -345,7 +332,7 @@ مزامنة تقييم %d / 10 - /?? + /\?\? /%d %s تم التوثيق تعذر تسجيل الدخول في %s @@ -355,7 +342,7 @@ الكل الحد الاقصي الحد الأدنى - @string/none + \@string/none الخطوط المحيطة النمط المنخفض ظل @@ -452,7 +439,7 @@ عرض مستودعات المجتمع قائمة عامة جميع الترجمات حروف كبيرة - تحميل جميع الإضافات من هذا المستودع? + تحميل جميع الإضافات من هذا المستودع\? %s (معطل) المسارات مسار الصوت @@ -533,4 +520,5 @@ سيتم تحديث التطبيق عند الخروج بدأ التحديث تم تنزيل الإضافة + إزالة من المشاهدة \ No newline at end of file diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 0fdda385..324f44bc 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -17,7 +17,8 @@ Визуализация на фона Скорост (%.2fx) Оценка: %.1f - Намерена е нова актуализация!\n%s -> %s + Намерена е нова актуализация! +\n%s -> %s Шаблон %d мин CloudStream @@ -105,7 +106,7 @@ Продължете да гледате Премахване Повече информация - @string/home_play + \@string/home_play Може да е необходим VPN, за да работи правилно този доставчик Този доставчик е торент, препоръчва се VPN Метаданните не се предоставят от сайта, зареждането на видео ще бъде неуспешно, ако не съществува на сайта. @@ -135,8 +136,7 @@ Докоснете два пъти от дясната или лявата страна, за да превъртите напред или назад Докоснете в средата, за да направите пауза Използвайте яркостта на системата - Използвайте системна яркост в плейъра на приложението вместо тъмно - наслагване + Използвайте системна яркост в плейъра на приложението вместо тъмно наслагване Актуализирайте прогреса на гледане Автоматично синхронизирайте прогреса на текущия си епизод Възстановете данните от архив @@ -175,8 +175,7 @@ Връзката е копирана в клипборда Пусни епизода Възстановяване на стойността по подразбиране - За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до - разработчиците + За съжаление приложението се срина. Анонимен доклад за грешка ще бъде изпратен до разработчиците Сезон %s %d%s Без сезон @@ -193,8 +192,10 @@ Продължи -30 30 - Това ще изтрие за постоянно %s\nСигурни ли сте? - %dm\nостава + Това ще изтрие за постоянно %s +\nСигурни ли сте\? + %dm +\nостава Продължава Завършен Статус @@ -223,8 +224,8 @@ Филм Серия Анимационен филм - @string/anime - @string/ova + \@string/anime + \@string/ova Торент Документален филм Азиатска драма @@ -320,7 +321,7 @@ Синхронизиране Оценен %d / 10 - /?? + /\?\? /%d %s удостоверен Не можах да вляза в %s @@ -426,7 +427,7 @@ Вижте хранилищата на общността Публичен списък Всички субтитри с главни букви - Изтегляне на всички добавки от това хранилище? + Изтегляне на всички добавки от това хранилище\? %s (Деактивиран) Потоци Аудио потоци diff --git a/app/src/main/res/values-bp/strings.xml b/app/src/main/res/values-bp/strings.xml index ebc3ecd2..2c2e1303 100644 --- a/app/src/main/res/values-bp/strings.xml +++ b/app/src/main/res/values-bp/strings.xml @@ -10,7 +10,7 @@ %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -20,7 +20,8 @@ Velocidade (%.2fx) Nota: %.1f - Nova atualização encontrada!\n%s -> %s + Nova atualização encontrada! +\n%s -> %s Filler %d min CloudStream @@ -107,7 +108,7 @@ Continue Assistindo Remover Mais Info - @string/home_play + \@string/home_play Uma VPN pode ser necessária para esse fornecedor funcionar corretamente Esse fornecedor é um torrent, uma VPN é recomendada Metadados não são oferecidas pelo site, o carregamento do video pode falhar se ele não existir no site. @@ -174,9 +175,7 @@ Link copiado para área de transferência Assistir Episódio Restaurar para o padrão - Desculpe, a aplicação travou. Um relatório de erro anônimo será enviado para os - desenvolvedores - + Desculpe, a aplicação travou. Um relatório de erro anônimo será enviado para os desenvolvedores Temporada Nenhuma Temporada Episódio @@ -190,8 +189,10 @@ Retomar -30 +30 - Isso apagará %s permanentemente\nVocê tem certeza? - %dm\nsobrando + Isso apagará %s permanentemente +\nVocê tem certeza\? + %dm +\nsobrando Em andamento Concluído Estado @@ -221,8 +222,8 @@ Filme Série Desenho Animado - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Documentário Drama Asiático @@ -277,22 +278,7 @@ Esticar Zoom Aviso Legal - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. Geral Botão Aleatório Mostra o botão Aleatório na página inicial @@ -337,7 +323,7 @@ Sincronizar Nota %d / 10 - /?? + /\?\? /%d %s autenticado Falha em autenticar para %s @@ -436,7 +422,7 @@ Ver repositórios da comunidade Lista pública Todas as legendas em maiúsculas - Transferir todos os plugins deste repositório? + Transferir todos os plugins deste repositório\? %s (Desativado) Reproduzir automaticamente próximo episódio Começa o próximo episódio quando o atual termina diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 5a2d44d4..87d5195e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -6,7 +6,7 @@ Hrají: %s Plakát - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -16,7 +16,8 @@ Rychlost (%.2fx) Hodnocení: %.1f - Nalezena nová aktualizace!\n%s -> %s + Nalezena nová aktualizace! +\n%s -> %s Výplň %d min CloudStream @@ -102,7 +103,7 @@ Pokračovat ve sledování Odebrat Další informace - @string/home_play + \@string/home_play Aby tento poskytovatel fungoval správně, budete možná potřebovat VPN Tento poskytovatel je torrent, je doporučená VPN Web neposkytnul žádná metadata, načítání videa selže, pokud na webu neexistuje. @@ -127,13 +128,10 @@ Dvojité klepnutí pro posun Dvojité klepnutí pro pozastavení Množství času k posunu - Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad - + Klepněte dvakrát vpravo nebo vlevo pro posun vpřed nebo vzad Klepněte doprostřed pro pozastavení Použít systémový jas - V přehrávači použít systémov - překrytí - + V přehrávači použít systémov překrytí Aktualizovat postup sledování Automaticky synchronizovat postup sledování současné epizody Obnovit data ze zálohy @@ -168,9 +166,7 @@ Odkaz zkopírován do schránky Přehrát epizodu Obnovit na výchozí hodnoty - Omlouváme se, aplikace spadla. Vývojářům bude odesláno anonymní hlášení - o pádu - + Omlouváme se, aplikace spadla. Vývojářům bude odesláno anonymní hlášení o pádu Sezóna Žádná sezóna Epizoda @@ -184,8 +180,10 @@ Pokračovat -30 +30 - Toto nevratně smaže %s\nJste si jisti? - %dm\nzbývá + Toto nevratně smaže %s +\nJste si jisti\? + %dm +\nzbývá Probíhající Dokončena Stav @@ -213,8 +211,8 @@ Film Seriál Animovaný - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Dokument Asijské drama @@ -265,22 +263,7 @@ Roztáhnout Přiblížit Odmítnutí odpovědnosti - Jakékoli právní otázky týkající se obsahu této aplikace - je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. - - V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. - - Aplikace je určena výhradně pro vzdělávací a osobní účely. - - CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. - CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani - nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, - uživatelsky přívětivém rozhraní. - - Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je - odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte - CloudStream 3 na vlastní nebezpečí. - + Jakékoli právní otázky týkající se obsahu této aplikace je třeba řešit se samotnými hostiteli a poskytovateli souborů, protože s nimi nejsme nijak spojeni. V případě porušení autorských práv se obraťte přímo na odpovědné strany nebo na webové stránky, na kterých se streamování odehrává. Aplikace je určena výhradně pro vzdělávací a osobní účely. CloudStream 3 v aplikaci nehostuje žádný obsah a nemá žádnou kontrolu nad tím, jaká média jsou v aplikaci umístěna nebo odstraněna. CloudStream 3 funguje jako jakýkoli jiný vyhledávač, například Google. Služba CloudStream 3 nehostuje, nenahrává ani nespravuje žádná videa, filmy ani obsah. Pouze vyhledává, agreguje a zobrazuje odkazy v pohodlném, uživatelsky přívětivém rozhraní. Pouze shromažďuje webové stránky třetích stran, které jsou veřejně přístupné prostřednictvím jakéhokoli běžného webového prohlížeče. Je odpovědností uživatele, aby se vyvaroval jakýchkoli akcí, které by mohly porušovat zákony platné v jeho lokalitě. Použijte CloudStream 3 na vlastní nebezpečí. Obecné Náhodné tlačítko Zobrazit na domovské stránce náhodné tlačítko @@ -322,7 +305,7 @@ Synchronizovat Hodnoceno %d / 10 - /?? + /\?\? /%d Přihlášeno k %s Nepodařilo se přihlásit k %s diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 707069d1..dc7088cc 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -159,7 +159,8 @@ Συνέχιση Αυτό θα διαγράψει μόνιμα το %s \nΕίστε σίγουροι πως θέλετε να προχωρήσετε; - %dm\nαπομένουν + %dm +\nαπομένουν Σε εξέλιξη Κατάσταση Έτος diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d6b48cd7..08ae5bf1 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -86,7 +86,7 @@ Recargar enlaces /\?\? /%d - Esto eliminará %s permanentemente + Esto eliminará %s permanentemente \nEstá seguro\? Está seguro que quiere salir\? Continuar Descarga diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index b1303012..a34c5a05 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -4,7 +4,11 @@ مکث در صف بارگیری + در حال بارگیری + اتمام بارگیری + پخش آنلاین بارگیری ناموفق بود + فیلتر کردن علاقه مندی ها جست‌وجو اندازه‌کردن پر کردن diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5402eabf..f0e112a8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -82,7 +82,8 @@ Supprimer Pause Reprendre - Cela va supprimer définitivement %s\nÊtes-vous sûr ? + Cela va supprimer définitivement %s +\nÊtes-vous sûr \? En cours Terminé Statut @@ -239,7 +240,7 @@ Continuer à regarder Retirer Plus d\'informations - @string/home_play + \@string/home_play Un VPN peut être nécessaire pour que ce fournisseur fonctionne correctement Ce fournisseur est un torrent, un VPN est recommandé Les métadonnées ne sont pas fournies par le site, le chargement de la vidéo échouera si elles n\'existent pas sur le site. @@ -269,9 +270,7 @@ Tapez deux fois sur le côté droit ou gauche pour aller en avant ou en arrière Tapez au milieu pour mettre en pause Utiliser la luminosité du système - Utiliser la luminosité du système dans le lecteur d\'applications au lieu du - sombre - + Utiliser la luminosité du système dans le lecteur d\'applications au lieu du sombre Mise à jour de la progression de la veille Synchronisation automatique de la progression de votre épisode en cours Restaurer des données à partir d\'une sauvegarde diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index a8816ce8..f33a2336 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -2,7 +2,8 @@ रफ्तार (%.2fx) - नया अपडेट आया है!\n%s -> %s + नया अपडेट आया है! +\n%s -> %s होम खोजें डाउनलोड @@ -69,8 +70,7 @@ प्लेयर में वीडियो की रफ्तार धिमी या तेज़ करता है दाएं या बाएं तरफ स्वाइप करने से वीडियो को आगे पीछे करता है दाएं तरफ या बाएं तरफ स्वाइप करने से रोशिनी और आवाज़ को ऊपर नीचे करता है - दो बार दाएं या बाएं तरफ दबाने से वीडियो को आगे या पीछे करा जा सकता है - + दो बार दाएं या बाएं तरफ दबाने से वीडियो को आगे या पीछे करा जा सकता है खोजें जानकारी नतीजों को सूत्रों के हिसाब से बांटकर दिखता है @@ -88,13 +88,13 @@ कोई लिंक नही मिले लिंक को क्लिपबोर्ड पे कॉपी करदिया गया चलाये - असुविधा के लिए खेद है, यह एप्प क्रैश हो गया है । एक गुमनाम रिपोर्ट निर्माताओं को भेज दी गयी है । - + असुविधा के लिए खेद है, यह एप्प क्रैश हो गया है । एक गुमनाम रिपोर्ट निर्माताओं को भेज दी गयी है । फ़ाइल डिलीट करें डिलीट रोके वापिस चलाये - एज इस चीज़ को हमेशा के लिए नष्ट कर देगा %s\nक्या आपका निर्णय निश्चित है ? + एज इस चीज़ को हमेशा के लिए नष्ट कर देगा %s +\nक्या आपका निर्णय निश्चित है \? अभी चालू है खत्म हो गया है स्तिथि @@ -132,8 +132,8 @@ ISP ब्लॉक से छुटकारा पाएं सूत्र की भाषाएं ऐप का लेआउट - पसंदीदा मीडिया - डाउनलोड करने का मार्ग + पसंदीदा मीडिया + डाउनलोड करने का मार्ग Dubbed या Subbed Anime दिखाये टीवी लेआउट फ़ोन लेआउट diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 2bbfaee6..837a2201 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -19,7 +19,7 @@ %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -29,7 +29,8 @@ Brzina (%.2fx) Ocijenjeno: %.1f - Pronađeno novo ažuriranje!\n%s -> %s + Pronađeno novo ažuriranje! +\n%s -> %s Umetak %d min CloudStream @@ -118,7 +119,7 @@ Nastavite s gledanjem Makni Više informacija - @string/home_play + \@string/home_play Za ispravan rad ovog pružatelja usluga može biti potreban VPN Ovaj pružatelj usluga je torrent, preporučuje se VPN Stranica ne daje metapodatke, učitavanje videozapisa neće uspjeti ako ne postoji na stranici. @@ -145,13 +146,10 @@ Dodirni dvaput za traženje Dodirni dvaput za pauziranje Iznos preskakanja u playeru - Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag - + Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag Dodirni u sredinu zaslona za pauziranje Koristi svijetlinu u sustavu - Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog - preklopa - + Koristi svjetlinu sustava u playeru aplikacija umjesto tamnog preklopa Ažuriraj napredak gledanja Automatski sinkroniziraj svoj trenutni napredak u epizodi Vraćanje podataka iz sigurnosne kopije @@ -190,8 +188,7 @@ Veza je kopirana u međuspremnik Pokreni epizodu Vrati na zadanu vrijednost - Nažalost, aplikacija se srušila. Anonimno izvješće o bugu bit će poslano developerima - + Nažalost, aplikacija se srušila. Anonimno izvješće o bugu bit će poslano developerima Sezona Nema sezone Epizoda @@ -207,8 +204,10 @@ Nastavi -30 +30 - Ovo će trajno izbrisati %s\nJeste li sigurni? - %dm\npreostalo + Ovo će trajno izbrisati %s +\nJeste li sigurni\? + %dm +\npreostalo U tijeku Završeno Status @@ -239,8 +238,8 @@ Film Serija Crtić - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Dokumentarac Azijska drama @@ -297,22 +296,7 @@ Rastegni Zoom Obavijest - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. Općenito Random gumb Prikaži random gumb na početnoj stranici @@ -351,7 +335,7 @@ Sinkroniziraj Ocijenjeno %d / 10 - /?? + /\?\? /%d Ovjereno%s Nije moguće prijaviti se na %s @@ -457,7 +441,7 @@ Pregledajte repozitorije zajednice Javni popis Svi titlovi pisani velikim slovima - Preuzeti sve dodatke iz ovog repozitorija? + Preuzeti sve dodatke iz ovog repozitorija\? %s (Onemogućeno) Zapis Audio zapis diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 5e515c56..83dc6ee9 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -5,7 +5,7 @@ Pemeran: %s Poster - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -15,7 +15,8 @@ Kecepatan (%.2fx) Dinilai: %.1f - Update baru ditemukan!\n%s -> %s + Update baru ditemukan! +\n%s -> %s Filler %d mnt CloudStream @@ -100,7 +101,7 @@ Lanjutkan Menonton Hapus Info lebih lanjut - @string/home_play + \@string/home_play Sebuah VPN mungkin diperlukan agar provider ini bisa bekerja dengan benar Provider ini adalah sebuah torrent, VPN direkomendasikan Metadata tidak disediakan oleh situs, loading video akan gagal jika tidak ada di situs. @@ -125,12 +126,10 @@ Tekan dua kali untuk mengubah waktu Tekan dua kali untuk menjeda Jumlah pengubah waktu pemutar - Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang - + Tekan dua kali di sisi kanan atau kiri untuk mengubah waktu ke depan atau ke belakang Tekan di tengah untuk menjeda Gunakan pencerahan sistem - Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap - + Gunakan pencerahan sistem di pemutar aplikasi dari pada hamparan gelap Update progres tontonan Sinkronisasikan progres episode saat ini secara otomatis Pulihkan data dari cadangan @@ -165,8 +164,7 @@ Tautan disalin ke papan klip Putar Episode Ulang ke pengaturan default - Maaf, aplikasi ngecrash. Laporan bug anonim akan dikirim ke developer - + Maaf, aplikasi ngecrash. Laporan bug anonim akan dikirim ke developer Season Tidak Ada Season Episode @@ -180,8 +178,10 @@ Lanjutkan -30 +30 - Ini akan secara permanen menghapus %s\nApakah anda yakin? - %dm\ntersisa + Ini akan secara permanen menghapus %s +\nApakah anda yakin\? + %dm +\ntersisa Masih Berlanjut Tamat Status @@ -209,8 +209,8 @@ Movie Seri Kartun - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Film Dokumenter Drama Asia @@ -261,22 +261,7 @@ Regang Zoom Peringatan - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. Umum Tombol Acak Tampilkan tombol acak di Beranda @@ -312,7 +297,7 @@ Sinkronisasi Skor %d / 10 - /?? + /\?\? /%d %s terautentikasi Gagal masuk ke %s @@ -498,7 +483,7 @@ Gerakan Beberapa perangkat tidak mendukung penginstal paket mode baru. Coba mode lama jika pembaruan tidak dapat diinstal. Aksi - Sumber + Referensi Ya Pasang dulu fitur tambahan Semua Bahasa @@ -526,4 +511,5 @@ Plugin Terunduh Aplikasi akan diperbaharui pada saat keluar Pembaharuan Dimulai + Hapus dari tontonan \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index f27f7c13..b4ba292e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -9,7 +9,7 @@ %d min Poster - @string/result_poster_img_des + \@string/result_poster_img_des Poster episodio Poster principale Prossimo casuale @@ -19,7 +19,8 @@ Velocità (%.2fx) Valutato: %.1f - Nuovo aggiornamento trovato!\n%s -> %s + Nuovo aggiornamento trovato! +\n%s -> %s Filler %d min @@ -107,7 +108,7 @@ Continua a guardare Rimuovi Più info - @string/home_play + \@string/home_play Potrebbe essere necessaria una VPN per far funzionare correttamente questo provider Questo provider è un torrent, si raccomanda una VPN I metadati non sono forniti dal sito, il caricamento del video fallirà se non esiste sul sito. @@ -193,8 +194,10 @@ Riprendi -30 +30 - Stai per eliminare permanentemente %s\nSei sicuro? - %dm\nrimanenti + Stai per eliminare permanentemente %s +\nSei sicuro\? + %dm +\nrimanenti In corso Completato Stato @@ -321,7 +324,7 @@ Sincronizza Valutato %d / 10 - /?? + /\?\? /%d %s autenticato Impossibile autenticarsi a %s @@ -428,7 +431,7 @@ Vedi le repository della community Lista pubblica Tutti i sottotitoli in maiuscolo - Scaricare tutti i plugin da questa repository? + Scaricare tutti i plugin da questa repository\? %s (Disabilitato) Tracce Traccia audio @@ -507,4 +510,5 @@ L\'app verrà aggiornata all\'uscita Aggiornamento avviato Plugin scaricato + Rimuovi dai già visti \ No newline at end of file diff --git a/app/src/main/res/values-kn/strings.xml b/app/src/main/res/values-kn/strings.xml index a6b3daec..efe0a1d8 100644 --- a/app/src/main/res/values-kn/strings.xml +++ b/app/src/main/res/values-kn/strings.xml @@ -1,2 +1,3 @@ - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 19d1bc6e..7251d0d7 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -3,7 +3,8 @@ Брзина (%.2fx) Оценето: %.1f - Пронајдена нова верзија на апликацијата!\n%s -> %s + Пронајдена нова верзија на апликацијата! +\n%s -> %s Филтер CloudStream Дома @@ -134,7 +135,8 @@ Избриши Паузирај Продолжи - Ова трајно ќе го избрише %s\nДали си сигурен? + Ова трајно ќе го избрише %s +\nДали си сигурен\? Во тек Завршени Статус diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index b60aadae..b6ad3a80 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -3,7 +3,8 @@ വേഗം (%.2fx) റേറ്റിംഗ്: %.1f - പുതിയ അപ്ഡേറ്റ്\n%s -> %s + പുതിയ അപ്ഡേറ്റ് +\n%s -> %s CloudStream ഹോം തിരയുക @@ -66,14 +67,14 @@ സ്രോതസ് അടിസ്ഥാനത്തിൽ തിരയുക തരം അടിസ്ഥാനത്തിൽ തിരയുക %d പഴം കൊടുത്തു - പഴം കൊടുത്തിട്ടില്ല + പഴം കൊടുത്തിട്ടില്ല റീസെറ് ചെയ്യാൻ അമർത്തിപ്പിടിക്കുക തുടർന്നു കാണുക നീക്കം ചെയ്യുക കൂടുതൽ വിവരം - ഈ സ്രോതസ് പ്രവൃത്തിക്കാൻ VPN ഉപയോഗിക്കേണ്ടിവന്നേക്കാം + ഈ സ്രോതസ് പ്രവൃത്തിക്കാൻ VPN ഉപയോഗിക്കേണ്ടിവന്നേക്കാം ഈ ടോറന്റ് സ്രോതസ് ഉപയോഗിക്കാൻ VPN ശുപാർശചെയ്യുന്നു വിവരണം വിവരണം ലഭ്യമല്ല @@ -109,7 +110,7 @@ പഴം കൊടുക്കു പഴം കൊടുത്ത എണ്ണം ആപ്പിന്റെ ഭാഷ - ഈ സ്രോതസ് ക്രോംകാസ്റ് അനുവദിക്കുന്നില്ല + ഈ സ്രോതസ് ക്രോംകാസ്റ് അനുവദിക്കുന്നില്ല ലിങ്കുകൾ ലഭ്യമല്ല ലിങ്ക് പകർത്തിയിരിക്കുന്നു എപ്പിസോഡ് പ്ലേയ് ചെയ്യുക @@ -122,7 +123,8 @@ ഡിലീറ്റ് നിർത്തുക തുടരുക - സ്ഥിരമായി %sനെ ഡിലീറ്റ് ചെയ്യുക\nഉറപ്പാണോ? + സ്ഥിരമായി %sനെ ഡിലീറ്റ് ചെയ്യുക +\nഉറപ്പാണോ\? തുടരുന്നു പൂർത്തിയായി അവസ്ഥ @@ -162,7 +164,7 @@ വലുപ്പം മാറ്റുക സ്രോതസ് OP ഒഴിവാക്കു - ഇനിയും കാണിക്കരുത് + ഇനിയും കാണിക്കരുത് അപ്ഡേറ്റ് ഔചിത്യ വീഡിയോ ക്വാളിറ്റി ചരിത്രം diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 60aa0bcf..c2561914 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -9,7 +9,7 @@ %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Aflevering Poster Hoofdposter Volgende willekeurig @@ -19,7 +19,8 @@ Snelheid (%.2fx) Beoordeeld: %.1fAls - Nieuwe update gevonden!\n%s -> %s + Nieuwe update gevonden! +\n%s -> %s Filler %d min CloudStream @@ -108,7 +109,7 @@ Doorgaan met kijken Verwijder Meer Info - @string/home_play + \@string/home_play Een VPN kan nodig zijn om deze provider correct te laten werken Deze provider is een torrent, een VPN wordt aanbevolen Metadata wordt niet geleverd door de site, het laden van video\'s zal mislukken als deze niet op de site bestaat. @@ -173,7 +174,7 @@ Link gekopieerd naar klembord Aflevering afspelen Reset naar standaardwaarde - Sorry, de applicatie is gecrasht. Er wordt een anoniem bugrapport naar de ontwikkelaars gestuurd + Sorry, de applicatie is gecrasht. Er wordt een anoniem bugrapport naar de ontwikkelaars gestuurd Seizoen Geen seizoen Aflevering @@ -189,8 +190,10 @@ Hervatten -30 +30 - Dit wordt zeker permanent verwijderd %s\nWeet u het zeker? - %dm\nremaining + Dit wordt zeker permanent verwijderd %s +\nWeet u het zeker\? + %dm +\nremaining Voortdurende Voltooid Status @@ -219,8 +222,8 @@ Film Serie Tekenfilm - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Documentaire Aziatisch drama @@ -256,7 +259,7 @@ Update Gewenste kijkwaliteit Maximaal aantal tekens voor titel van videospeler - Videospeler Resolutie + Videospeler Resolutie Grootte videobuffer Lengte videobuffer Video cache op schijf @@ -319,7 +322,7 @@ Sync gewaardeerd %d / 10 - /?? + /\?\? /%d Geauthenticeerd %s Mislukt om te verifiëren aan %s @@ -394,7 +397,7 @@ Instelling overslaan Pas het uiterlijk van de app aan uw apparaat aan Crashrapportage - Wat wil je zien? + Wat wil je zien\? Klaar Markeer als bekeken Geschiedenis diff --git a/app/src/main/res/values-no/strings.xml b/app/src/main/res/values-no/strings.xml index f3807952..41bf704d 100644 --- a/app/src/main/res/values-no/strings.xml +++ b/app/src/main/res/values-no/strings.xml @@ -2,7 +2,7 @@ Plakat - @string/result_poster_img_des + \@string/result_poster_img_des Episode Plakat Main Plakat Neste tilfeldig @@ -12,7 +12,8 @@ Avspillingshastighet (%.2fx) Vurdert: %.1f - Ny oppdatering funnet!\n%s -> %s + Ny oppdatering funnet! +\n%s -> %s CloudStream Hjem Søk @@ -142,7 +143,8 @@ Slett Stopp Gjenoppta - Dette vil slette %s\nEr du sikker? + Dette vil slette %s +\nEr du sikker\? Pågående Fullført Posisjon @@ -201,22 +203,7 @@ App Tema Foretrukket Videoinnhold Disclaimer - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. Besetning: %s %dm Tøm diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index ed079c58..244ae2e1 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -2,16 +2,17 @@ Prędkość (%.2fx) Ocena: %.1f - Znaleziono nową aktualizację!\n%s -> %s + Znaleziono nową aktualizację! +\n%s -> %s Filler %d min %s Odc. %d - Plakat - Plakat odcinka - Główny plakat + Obrazek + Obrazek odcinka + Główny obrazek Następny losowy Wstecz - Zmień dostawcę + Zmień źródło Pogląd tła CloudStream Główna @@ -23,7 +24,7 @@ Brak danych Więcej opcji Następny odcinek - Odcinek %d będzie udostępniony + Odcinek %d wyjdzie za Gatunki Udostępnij Otwórz w przeglądarce @@ -52,11 +53,11 @@ Błąd przy pobieraniu Anulowano pobieranie Zakończono pobieranie - Stream + Odtwórz Błąd przy ładowaniu linków Pamięć wewnętrzna Dub - Sub + Nap Usuń plik Odtwórz plik Wznów pobieranie @@ -86,7 +87,7 @@ Wzniesienie napisów Czcionka Rozmiar czcionki - Szukaj według dostawców + Szukaj według źródeł Szukaj według typów Dano %d bananów Brak bananów @@ -94,12 +95,12 @@ Pobieranie języków Język napisów Przytrzymaj, aby zresetować - Importuj czcionki umieszczając je w %s + Importuj czcionki, umieszczając je w %s Kontyntynuj oglądanie Usuń Więcej informacji Połączenie przez VPN może być wymagane - Ten dostawca jest torrentem, wskazane jest użycie połączenia VPN + To źródło jest torrentem, wskazane jest użycie połączenia VPN Metadane nie są dostarczane przez witrynę, ładowanie filmu nie powiedzie się, jeśli nie ma ich w witrynie. Opis Nie znaleziono opisu @@ -107,25 +108,25 @@ Pokaż Logcat 🐈 Obraz-w-obrazie Oglądaj w małym, pływającym okienku - Przycisk zmiany rozmiaru + Zmień rozmiar odtwarzacza Usuwanie czarnych ramek Napisy Ustawienia napisów Napisy Chromecast Ustawienia napisów Chromecast - Tryb Eigengrau + Tryb Eigengravy Ustawienia prędkości - Przesuwaj aby przeglądać - Przesuwaj w lewo lub prawo aby kontrolować czas - Przesuwaj aby zmienić ustawienia - Przesuwaj po lewej lub prawej stronie aby zmienić jasność i głośność + Przesuń aby przewinąć + Przesuń w lewo lub prawo aby kontrolować czas + Przesuń aby zmienić ustawienia + Przesuń góra-dół z lewej lub prawej aby zmienić jasność i głośność Autoodtwarzanie następnego odcinka - Rozpocznij następny odcinek po zakończeniu bieżącego - Wielkość skoku przy podwójnym stuknięciu - Podwójne stuknięcie aby przeglądać - Stuknij 2 razy z prawej lub lewej strony aby przeglądać - Stuknij dwukrotnie, aby wstrzymać - Stuknij na środku, aby wstrzymać + Rozpocznij następny odcinek po skończeniu bieżącego + Czas przewinięcia przy podwójnym kliknięciu + Podwójne kliknięcie aby przewinąć + Kliknij 2 razy z prawej lub lewej strony aby przewinąć + Kliknij dwukrotnie aby wstrzymać + Kliknij na środku, aby zatrzymać wideo Użyj jasności systemowej Użyj jasności systemowej w odtwarzaczu aplikacji zamiast ciemnej nakładki Aktualizuj postęp oglądania @@ -142,23 +143,23 @@ Aktualizacje i kopia zapasowa Informacje Zaawansowane wyszukiwanie - Szukaj z podziałem na dostawców + Szukaj z podziałem na źródła Wysyłaj dane tylko przy awariach Nie wysyłaj żadnych danych - Pokaż odcinek wypełniający dla anime + Pokaż fillery dla anime Pokaż zwiastuny - Pokaż plakaty z Kitsu + Pokaż obrazki z Kitsu Ukryj wybraną jakość wideo w wynikach wyszukiwania Automatyczne aktualizacje rozszerzeń Automatyczne pobieranie rozszerzeń Pokazuj aktualizacje - Automatycznie wyszukuj aktualizacji przy starcie + Automatycznie wyszukuj aktualizacje przy starcie Aktualizuj do wersji beta - Wyszukuj wersji beta, zamiast pełnych wydań + Wyszukuj wersji beta, zamiast oficjalnych wydań Github - Aplikacja do noweli - Aplikacja do anime - Discord + Aplikacja do light novel od nas + Aplikacja do anime od nas + Serwer Discord Daj banana programistom Dano banana Język aplikacji @@ -167,7 +168,7 @@ Skopiowano do schowka Odtwórz odcinek Zresetowano - Awaria aplikacji. Anonimowe zgłoszenie błedu zostanie wysłane programistom + Awaria aplikacji. Anonimowe zgłoszenie błędu zostanie wysłane programistom Sezon %s %d%s Brak sezonu @@ -184,9 +185,11 @@ Odtwórz -30 +30 - Spowoduje to trwałe usunięcie %s\nCzy jesteś pewien? - %dm\npozostało - Bierzący + Spowoduje to trwałe usunięcie %s +\nCzy jesteś pewien\? + %dm +\npozostało + Bieżący Zakończone Status Rok @@ -202,13 +205,13 @@ Aplikacja Filmy - Serial telewizyjny + Seriale telewizyjne Kreskówki Anime Torrenty Filmy dokumentalne OVA - Filmy azjatyckie + Dramy azjatyckie Transmisje na żywo NSFW Inne @@ -218,7 +221,7 @@ Kreskówka Torrent Film dokumentalny - Film azjatycki + Drama azjatycka Transmisja na żywo NSFW Inne @@ -226,48 +229,48 @@ Zdalny błąd Błąd renderowania Nieoczekiwany błąd odtwarzacza - Błąd pobierania, sprawdź uprawnienia - Chromecast odcinka - Chromecast mirroru + Błąd pobierania, sprawdź uprawnienia aplikacji + odcinek Chromecast + mirror dla Chromecast Odtwórz w aplikacji Odtwórz w %s Odtwórz w przeglądarce Kopiuj link Automatyczne pobieranie Pobierz mirror - Odświerz linki + Odśwież linki Pobierz napisy Etykieta jakości Etykieta dubbingu Etykieta napisów Tytuł - Włącz elementy interfejsu na plakatach + Pokaż elementy interfejsu na obrazkach Nie znaleziono aktualizacji - Sprawdź czy jest aktualizacja + Sprawdź, czy jest aktualizacja Zablokuj - Rozmiar - Źródlo + Zmień rozmiar + Źródło Pomiń OP Nie pokazuj ponownie Pomiń tę aktualizację Aktualizacja Domyślna jakość - Maksymalna ilość znaków tytułu w odtwarzaczu - Zawartość tytułu w odtwarzaczu + Maksymalna ilość znaków w tytule odtwarzacza + Rozdzielczość odtwarzacza wideo Rozmiar bufora wideo Długość bufora wideo Pamięć podręczna wideo na dysku Wyczyść pamięć podręczną wideo i obrazów - Ustawienie zbyt wysokiej wartości może powodować problemy w systemach z małą ilością pamięci RAM, takich jak urządzenia Android TV lub stare telefony. - Zbyt wysokie ustawienie może powodować problemy w systemach z małą ilością miejsca w pamięci, takich jak urządzenia Android TV. - DNS przez HTTPS + Ustawienie zbyt wysokiej wartości może powodować problemy na urządzeniach z małą ilością pamięci RAM, takich jak urządzenia Android TV lub stare telefony. + Zbyt wysokie ustawienie może powodować problemy na urządzeniach z małą ilością dostęponej pamięci, takich jak urządzenia Android TV. + DNS over HTTPS Przydatne w pomijaniu blokad dostawców internetu Sklonuj stronę Usuń stronę Dodaj klona istniejącej strony z innym adresem URL Ścieżka pobierania URL serwera Nginx - Wyświetlanie Anime z dubbingiem/subbingiem + Wyświetlanie Anime z dubbingiem/napisami Dopasuj do ekranu Rozciągnij Powiększ @@ -275,12 +278,12 @@ Ogólne Przycisk do losowania Pokaż przycisk do losowania na stronie głównej - Języki dostawców + Języki źródeł Układ aplikacji Preferowane media - Włącz NSFW u obsługiwanych dostawców + Włącz NSFW w obsługiwanych źródłach Kodowanie napisów - Dostawcy + Źródła Układ interfejsu Automatyczny Układ dla telewizorów @@ -288,8 +291,8 @@ Układ dla emulatorów Kolor podstawowy Motyw aplikacji - Pozycja tytułu względem plakatu - Ustaw tytuł pod plakatem + Pozycja tytułu względem obrazka + Ustaw tytuł pod obrazkiem Kod języka (pl) %s %s konto @@ -301,7 +304,7 @@ Dodaj synchronizację Dodano %s Synchronizacja - Ocenione + Oceniono %d na 10 Uwierzytelniono %s Nie udało się uwierzytelnić %s @@ -312,9 +315,9 @@ Maks Min Kontur - Wciśnięte + Obniżone Cień - Zniesione + Wzniesione Synchronizacja napisów 1000 ms Opóźnienie napisów @@ -327,7 +330,7 @@ Wczytaj z pliku Wczytaj z Internetu Pobrano plik - Główna + Główny Drugoplanowy Pomocniczy Źródło @@ -349,7 +352,7 @@ HDR SDR Web - Obraz plakatu + Obrazek Odtwarzacz Rozdzielczość i tytuł Tytuł @@ -358,26 +361,26 @@ Niepoprawne dane Niepoprawny URL Błąd - Usuń informacje dla niesłyszących z napisów + Usuń CC z napisów Usuń nadmiarowe informacje z napisów - Filtrowanie wg preferowanego języka mediów + Filtrowanie wg preferowanego języka Dodatki Zwiastun Odsyłacz - Dalej - Wyświetlaj filmy w wybranych językach - Cofnij - Pomiń + Następny + Wyświetlaj filmy w tych językach + Poprzedni + Pomiń setup Dostosuj wygląd aplikacji do urządzenia - Przekazywanie błędów - Preferowany rodzaj filmów + Zgłaszanie błędów + Co chciałbyś obejrzeć\? Gotowe - Dodatki + Rozszerzenia Dodaj repozytorium Nazwa repozytorium Adres URL repozytorium - Rozszerzenie załadowane - Rozszerzenie usunięte + Załadowano rozszerzenie + Usunięto rozszerzenie Błąd ładowania %s 18+ Rozpoczęto pobieranie %d %s… @@ -395,24 +398,24 @@ Zaaktualizowano %d rozszerzeń CloudStream nie ma domyślnie zainstalowanych żadnych witryn. Musisz zainstalować witryny z repozytoriów. \n -\nZ powodu bezmyślnego usunięcia DMCA przez Sky UK Limited 🤮 nie możemy zamieścić linku dowitryny z repozytoriami. +\nZ powodu bezmyślnego usunięcia DMCA przez Sky UK Limited 🤮 nie możemy zamieścić linku do witryny z repozytoriami. \n -\nDołącz do naszego Discorda lub szukaj online. +\nDołącz do naszego Discorda lub poszukaj online. Zobacz repozytoria społeczności Publiczna lista Wszystkie napisy wielką literą - Pobrać wszystkie rozszerzenia z tego repozytorium? + Pobrać wszystkie rozszerzenia z tego repozytorium\? %s (Wyłączone) Ścieżki Ścieżki audio Ścieżki wideo Zastosuj po ponownym uruchomieniu Tryb bezpieczny włączony - Wszystkie rozszerzenia zostały wyłączone z powodu awarii, aby pomóc Ci znaleźć ten, który powoduje problemy. - Wyświetl informacje o awarii + Z powodu wystąpienia błędu wszystkie rozszerzenia zostały wyłączone, aby ułatwić wykrycie tego wadliwego. + Wyświetl informacje o błędzie Ocena: %s Opis - Versja + Wersja Status Rozmiar Autorzy @@ -431,7 +434,7 @@ Wyczyść historię Historia Za dużo tekstu. Nie można skopiować do schowka. - Link do transmisji + Link do odtwarzania Odtwórz w CloudStream Pomiń %s %dh %dm @@ -446,11 +449,11 @@ Pobieranie aktualizacji aplikacji… /%d Obsada: %s - Automatycznie instaluj wszystkie jeszcze nie zainstalowane wtyczki z dodanych repozytoriów. + Automatycznie instaluj wszystkie jeszcze niezainstalowane wtyczki z dodanych repozytoriów. \@string/result_poster_img_des Podsumowanie Instalator APK - Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb kompatybilności, jeśli aktualizacje nie zostaną zainstalowane. + Niektóre telefony nie obsługują nowego instalatora pakietów. Wypróbuj tryb legacy, jeśli aktualizacje nie zostaną zainstalowane. password123 \@string/ova MojaFajnaWitryna @@ -470,12 +473,12 @@ Intro Mixed ending Pokaż wyskakujące okienka pomijania dla niektórych segmentów - Dodatki + Rozszerzenia Działania Pamięć podręczna Powtórz proces konfiguracji Linki - Aktualizacje aplikacji + Aktualizacje Kopia zapasowa Napisy Gesty @@ -486,7 +489,7 @@ Wygląd Odtwórz zwiastun Aplikacja zostanie zaktualizowana po wyjściu - Aktualizacja rozpoczęta - Wtyczka pobrana + Rozpoczęto aktualizację + Pobrano rozszerzenie Usuń z obejrzanych \ No newline at end of file diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 239d144e..28673fe2 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -9,7 +9,7 @@ %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Poster Episod Poster Principal Următorul la Întâmplare @@ -19,7 +19,8 @@ Viteză (%.2fx) Evaluare: %.1f - Actualizare nouă găsită!\n%s -> %s + Actualizare nouă găsită! +\n%s -> %s Filler %d min @@ -105,7 +106,7 @@ Continuați să urmăriți Eliminați Mai multe informații - @string/home_play + \@string/home_play Există probabilitatea necesitații unui VPN pentru ca acest furnizor să funcționeze corespunzător Acest furnizor este un torrent, se recomandă un VPN Metadatele nu sunt furnizate de către site, există posibilitatea ca încărcarea videoclipului să eșueze. @@ -184,8 +185,10 @@ Continuă -30 +30 - Sunteți pe cale să ștergeți definitiv %s\nSunteți sigur? - %dm\nrămas + Sunteți pe cale să ștergeți definitiv %s +\nSunteți sigur\? + %dm +\nrămas În curs de desfășurare Finalizat Status @@ -268,22 +271,7 @@ Întindere Mărire Aviz juridic (declinarea responsabilității și drepturi de autor) - Orice probleme legale privind conținutul acestei aplicații ar trebui - să fie rezolvate cu furnizorii și gazdele actuale de fișiere, întrucât noi nu suntem afiliați cu aceștia. - - În caz de încălcare a drepturilor de autor, vă rugăm să contactați direct părțile responsabile sau site-urile de streaming. - - Aplicația este destinată exclusiv utilizării educaționale și personale. - - CloudStream 3 nu găzduiește niciun fel de conținut în aplicație și nu are niciun control asupra conținutului media care este pus sau retras. - CloudStream 3 funcționează ca orice alt motor de căutare, cum ar fi Google. CloudStream 3 nu găzduiește, nu încarcă și - nu gestionează niciun videoclip, film sau conținut. Pur și simplu navighează, adună și afișează linkuri într-o interfață convenabilă - și ușor de utilizat. - - Pur și simplu, acesta extrage paginile web ale unor terțe părți care sunt accesibile publicului prin intermediul oricărui browser web obișnuit. Este - responsabilitatea utilizatorului de a evita orice acțiune care ar putea încălca legile care guvernează locația sa. Utilizați - CloudStream 3 pe propria răspundere. - + Orice probleme legale privind conținutul acestei aplicații ar trebui să fie rezolvate cu furnizorii și gazdele actuale de fișiere, întrucât noi nu suntem afiliați cu aceștia. În caz de încălcare a drepturilor de autor, vă rugăm să contactați direct părțile responsabile sau site-urile de streaming. Aplicația este destinată exclusiv utilizării educaționale și personale. CloudStream 3 nu găzduiește niciun fel de conținut în aplicație și nu are niciun control asupra conținutului media care este pus sau retras. CloudStream 3 funcționează ca orice alt motor de căutare, cum ar fi Google. CloudStream 3 nu găzduiește, nu încarcă și nu gestionează niciun videoclip, film sau conținut. Pur și simplu navighează, adună și afișează linkuri într-o interfață convenabilă și ușor de utilizat. Pur și simplu, acesta extrage paginile web ale unor terțe părți care sunt accesibile publicului prin intermediul oricărui browser web obișnuit. Este responsabilitatea utilizatorului de a evita orice acțiune care ar putea încălca legile care guvernează locația sa. Utilizați CloudStream 3 pe propria răspundere. General Aleatoriu Afișați butonul Aleatoriu pe pagina de start @@ -320,7 +308,7 @@ Sincronizare Recenzie %d / 10 - /?? + /\?\? /%d %s autentificat Imposibil de autentificat la %s diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 49ed0fb3..930437c7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -4,9 +4,9 @@ Нет Да Треки - в очереди + В очереди Приблизить - Скачать + Загрузить Поиск Заполнить Скачать неудачный @@ -16,7 +16,7 @@ Пауза Актёрский состав: %s Название источника - Вход + Войти Нет Название Загрузка… @@ -29,7 +29,7 @@ Главный плакат Следующий случайный Вернуться - Сменить поставщика + Изменить поставщика Фон предпросмотр Скорость (%.2fx) Оценили: %.1f @@ -39,7 +39,7 @@ CloudStream Убирать %s Ep %d - Проиграть с CloudStream + Смотреть с CloudStream Главная Поиск Загрузки @@ -50,19 +50,19 @@ Дополнительные опции Следующий серия Жанры - Делиться + Поделиться Открыть в браузер - Скип загрузка + Пропустить загрузку Смотрю - Приостановленный + Приостановленно Завершено Брошенный План по смотреть Никто Пересмотрю - Проиграть фильм + Смотреть фильм Проиграть трейлер - Проиграть Livestream + Воспроизвести Livestream Источники Субтитры Проиграть серия @@ -77,8 +77,8 @@ Инфо Обновление началось Прятать - Проиграть - Подробнее инфо + Смотреть + Подробнее Фильтр закладки Закладки Наносить @@ -88,7 +88,7 @@ Очистить Сохранить Скорость проигрыватель - Проиграть Серия + Воспроизвести Эпизод %dд %dч %dм %d мин Dub @@ -101,14 +101,14 @@ Цвет окна Тип края Субтитр подъём - Поиск с использованием поставщики + Поиск с использованием поставщиков Поиск с использованием типов %d Бенены данность на разрабы Бенены не дают Автовыбор языка Скачать языки Язык субтитров - Удерживайте чтобы сбросить по умолчанию + Удерживайте, чтобы сбросить по умолчанию Ошибка загрузки ссылок Поток Шрифт @@ -120,9 +120,9 @@ Приостановить скачать Отключить автоматическое информирование об ошибках Импортируйте шрифты поместив их в %s - Продолжать смотрю + Продолжить смотреть Удалите - Дополнительная инфо + Подробнее Для корректной работы этого поставщика может потребоваться VPN Этот поставщика - торрент, рекомендуется VPN Метаданные не предоставляются сайтом, загрузка видео будет неудачной, если они не существуют на сайте. @@ -151,7 +151,7 @@ Не удалось восстановить данные из %s Отсутствует разрешение на хранение. Пожалуйста попробуйте снова. Аккаунты - Обновления и резервное копирование + Обновления и резервное Информация Расширенный поиск Показывать трейлеры @@ -166,8 +166,8 @@ Язык приложения Ссылок не найдено Ссылка скопирована в буфер обмена - Восстановить к изначальному значению - Извините, приложение прекратило работу. Анонимный отчет об ошибке был отправлен разработчикам + Восстановить по умолчанию + Извините, приложение прекратило работу. Анонимный отчет об ошибке будет отправлен разработчикам Эпизод Эпизоды С @@ -200,4 +200,212 @@ Автоматически загружать еще не установленные плагины из добавленных репозиториев. Присоединится в Discord Бесплатно + %d мин. + %d ч. %d мин. + Фильмы + Мультфильм + Сериалы + Азиатские драмы + Видео + Мультфильмы + Документальные фильмы + OVA + NSFW + NSFW + Фильм + Серия + Торрент + Документальный + Азиатская драма + Общий + Провайдеры + Макет + Расширения + Плеер + Резервное копирование данных + Отправлять данные только при вылетах + Использовано + Двойное нажатие для паузы + Коснитесь дважды правой или левой стороны для поиска вперед или назад + Нажмите в центре для паузы + Использовать системную яркость + Автоматически синхронизировать текущий прогресс эпизода + Ошибка резервного копирования %s + Восстановить данные из резервной копии + Показывать Филлер эпизоды для аниме + Показывать афиши из Kitsu + Некоторые телефоны не поддерживают новые установщики. Попробуйте использовать legacy (старый) вариант, если обновления не устанавливаются. + Выходит + Блокировка + Обновление не найдено + Изменить размер + Источник + Проверьте наличие обновления + Клон сайта + DNS через HTTPS + Удалить сайт + Оговорка + Синхронизация субтитров + Добавить клон существующего сайта с другим URL-адресом + Используется для обхода блокировок интернет провайдера + Путь загрузки + учитывая бенен + Обновить + Основной цвет + Языки поставщиков + Название репозитория + Очистить историю + Referer + Дайте бенен разрабам + Ссылки + Макет + Макет приложения + Тема приложения + Добавить репозиторий + Убрать отметку + Вы уверены, что хотите выйти\? + Плагин скачан + Плагин удалён + Описание + Версия + Состояние + Размер + Авторы + Поддерживается + VLC + MPV + Пропустить %s + Концовка + Используйте яркость системы в проигрывателе приложения вместо темного наложения + Обновить состояние хода просмотра + Данные сохранены + Дает вам результаты поиска, разделенные по провайдеру + Поиск предварительных обновлений вместо полных выпусков + Повторить процесс настройки + Этот провайдер не поддерживает Chromecast + %s %d%s + Нет сезона + %d-%d + %d %s + Прямые трансляции + Прямая трансляция + Ошибка источника + Ошибка пульта + Ошибка рендера + Неожиданная ошибка плеера + Эпизод Chromecast + Воспроизведение на %s + Воспроизвести в браузере + Скачать субтитры + Знак качества + Переключение элементов интерфейса на плакате + Пропустить OP + Больше не показывать + Пропустить это обновление + URL-адрес NGINX-сервера + Создать учётную запись + Добавить трекинг + Добавлено %s + Синхронизировать + Оценено + %d из 10 + Посмотреть информацию о сбое + Предпочитаемый видео-проигрыватель + Веб-браузер + Приложение не найдено + Все языки + Вступление + Титры + Отметить как просмотренное + Разрешение видеоплеера + Желаемое качество видео + Максимум символов + Длинна буфера + Кеш видео на диске + Размер буфера + Отчистить кеш видео и изображений + Вызывает сбои, если установлено слишком высокое значение на устройствах с небольшим объемом памяти, таких как Android TV. + Вызывает проблемы, если установлено слишком высокое значение на устройствах с небольшим объемом памяти, таких как Android TV. + Легкая новелла от тех же разработчиков + Язык + HLS Плейлист + Сначала установить расширение + Внутренний проигрыватель + Синопсис + Интро + hello@world.com + Зеркало Chromecast + Воспроизвести в приложении + По умолчанию + Возможности плеера + Субтитры + Расположение названия плаката + TV + Телефон + Эмулятор + Под плакатом + parol123 + МоёИмяПользователя + Сменить учётную запись + Добавить учётную запись + МойКрутойСайт + example.com + Язык (en) + учётная запись + Автоматически + 127.0.0.1 + Обновления приложения + Резервная копия + Действия + Кэш + Жесты + Готово + Расширения + URL репозитория + Плагин загружен + \@string/home_play + Перемотка двойным касанием + /\?\? + /%d + 18+ + Не удалось загрузить %s + плагины + SDR + TS + Удалить репозиторий + SD + 4K + Web + UHD + плагин + Пропустить настройку + Ошибка + Cam + Cam + HDR + DVD + WP + TC + HD + Blu-ray + Cam + HQ + Отключено: %d + %s %s + %s аутентифицировано + Не удалось перейти к %s + Максимум + Минимум + Очертание + Тень + 1000 мс + Задержка субтитров + Без задержки субтитров + Используйте, если субтитры отображаются на %d мс слишком рано + Используйте, если субтитры отображаются %d мс слишком поздно + Нормально + Поднятие + Съешь ещё этих мягких французских булок, да выпей же чаю + Рекомендуется + Загружено %s \ No newline at end of file diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index dd3aa432..8bb22e66 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -7,14 +7,484 @@ %s Ep %d Xalqadda %d waxa lasoo deyn doonaa Daji - Dajintii ma guulaysan + Dejintii ma guulaysan Raadi Tirtir - Qabo - la horay - La ekaysii - Ku buuxi + Haki + Horran + Le-ekaysii shaashadda + Kala jiid Soo dhawee Dhammaan Muusik + Ku daawo CloudStream + Habaynta + Buuxiye + Beddel qaybiyaha + Cloudstream + Xawaaraha (%.2fx) + %d dqq + Kadinka + Qiimaysan: %.1f + App cusub baa soo baxay! +\n%s -> %s + Soo dejinta + Raadi + Dookhyo kale + Xog ma leh + Xalqadda xigta + Daawanaysid + Raadi %s… + Caynadda + Faafi + Ku fur barawsarka + Is dhaafi soo kicitaanka + Soo kacaya… + Raadi… + Kor u hay + Dib u daawasho + Dhamaystirmay + Soo dhacay + Ku talo jira + Midna + Daaro filinka + Daar goos-gooska + Midabka daaqadda + Saar faylka + Daar faylka + Si toos u daawo + Daalaco toorentiga + Qrl-hoosaadka + Dib u bilow dejinta + Jooji + Midabka dambeedka + Xigashooyinka + Dib u xidhiidhinaya… + Dib u noqo + Xalqadda daaro + Waad hakisey la degista + Way dhamaatay dejintu + Kaydka gudaha + Trj + Way bilaabmatay soo dejintu + Daalaco + Jooji cillad gudbinta iskeed ah + Waa la joojiyey dejintan + Way bilaabantay cuaboonaysiintu + Soo raridda lifaaqyadu way fashilantay + Qrl + Qari + Warbixin + Warbixin dheeraad ah + Habaynta Qoraal-hoosaadka + Midabka lakabka sare + Daar + Shaandhee caalamad-sanayaasha + Calaamad-sanayaasha + Xagee marinaysaa + Kaydi + Xawaaraha daaraha + Xajmiga farta + Haki dejinta + Ka dhig + Midabka farta + Xeji si aad asalkiisa ugu soo celiso + Koobi/ka reeb + Xidh + Tirtir + Nooca cidhifyada + Isticmaal xaddiga iftiinka ee moobilkaaga halkii aad adigu habayn lahayd ugu horreynba + Gurmadka iyo cusbooneysiinta + Waa la kaydiyet xogta gurmadka + Raadinta waxay kuugu kala qaybinaysaa mid kasta iyo qaybiyihiisa + Ma jirto xog la dirayo + Way fashilantay in xogta laga soo celiyo faylka gurmadka%s + Moobillada qaar ayaa awoodin iney Rakibaha cusub een isticmaalnay, kan ku day haddii cusbooneysiintu kuu shaqayn weydo. + +30 + Xogta waa la kaydiyey + Heerka + Ogolaanshaha lagu kaydinayo ma hayno. Fadlan isku day mar kale. + Koontooyinka + Xog + Si iskii ah usoo deji sidkanaha + Qari tayada muqaalka aad dooratay natiijada waxa aad raadiso + Raadin heer-sare ah + Keliya xogta waxaa ka dirayaa marka appku duqeeyo + Xalqad buuxis ah tusi anemiga + Dhiseyaasha appka sii beniin + Way fashilantay dejintu, hubi ogolaanshaha isticmaalka kaydka + Xalqadda koromakaastiga + Soo dejiso Qoraal-hoosaadyada + Summadda Tayada + Humaagga-bixinta koromokaastiga + Asalkiisa + Isla appkan ku daawo + Ku daawo %s + Ku daawo barawsarka + Dejinta iskeed ah + Deji toorentiga(mirror) + Cusbooneysii lifaaqyada + Eeg iney jirto cusbooneysiin + Summadda codeynta + Sumadda Qrl-hoosaadka + Ciwaanka + Maxaa ka muuqan doona boodhka filinka + Ma jiro wax ku cusub Appka + Quful + Cabbirka + isha + Is dhaafi + Mar dambe ha i tusin + Is dhaafi + Cusbooneysii + Tayada aad doorbiddo + Qaabka + Qaab emulator + Waxa uu ku tusayaa badhin aad si xukasho la\'aan ah ugu dooran kartid + Kor-u-baxa Qrl-hoosaadka + Farta + Muuq dambeedka + Ku raadi qaybiyeyaasha + Ku raadi noocyada + %d oo beniin ayaad siisey dhiseyaasha + Wax beniin ah maad bixin + Iskeed-u-doorashada luuqadda + Luuqadda dejinta + Luuqadda Qoraal-hoosaadka + Xogta muqaalkan ma bixinno, ma shaqayn doonto haddii aannu weyno xogta muqaalka. + Dhig nooca farta aad dooneyso %s + Sii wado daawashada + Ka saar + Xog dheeri ah + Tusi Boodhka Kitsu + Waxa laa raadinayaa haddii ay wax jiraan baahin-horaad ah, halkii la sugi lahaa baahin buuxda + Si iskii ah u cusbooneysii sidkanaha + Si iskii ah usoo deji dhamaan sidkanayaasha aan weli lasoo dejin ee ku jira kaydka aad keentay. + Marka ugu horreysa ee aad appka furto ayaad arkaysaa haddii wax cusub yihiin + Light novel app, isla dhiseyaasha appkan baa leh + Tus wixii appka ku cusub + Dib uso bilow fadhiisinta appka + %d %s + Ku cusbooneysii baahinta ugu horreysa + Rakibaha APKga + Socda + Github + Filin + Anime app, isla dhiseyaashan appkan baa leh + Kal ma leh + Discord naga soo qabo + Beeniinyada aad bixisey + Luuqadda + Bixiyahan kuma shaqeeyo koromakaastiga + Asalkiisii hore kusoo celi + Dulucuda + Raalliahow, wuu duqeeyey appku, waxa warbixin cillad-saarka ka caawisa loo direy dhiseyaasha + Kalka + %s %d%s + Xalqad + Xalqado + %d illaa %d + K + X + Kartoon + Daraamada eeshiyaanka + Tirtir faylka + Sii wado + -30 + Dhamaantii waa la saari doona %s +\nSow ma hubtid\? + Fashil ka yimi xigashada + %ddq +\nAyaa hadhsan + Dhamaystirmay + Sannadka + Qiimaynta + Muddada + La isticmaalay + Qrl-hoosaad ma leh + Sidiisaa + Bilaash + Appka + Kartoob + Anemi + Daaraha ayaa ku fashilmay + OVA + Toorenti + Tooska + OVA + Daraamada Eeshiyaanka + Daaraha korkiisa + kaydka kumeelgaadhka + Ka reebo fadhigan + Kuwa kale + Taxane + Anemi + Dhokumentari + Toorentiyo + Dhokumentariyo + Toos + CEEB + Kuwa kale + Fashil fog + DNS ka horreysi HTTPS + Maxaa ku cusub appka + Gurmad + Kordhiyeyaal + Qoraal-hoosaadyada + Muqaal + Jiid si aad u habaysato + Laga yaabaa inaad u baahato VPN si qaybiyahan kuugu shaqeeyo + Qaybiyahan waa toorenti, inaad VPN isticmaasho ayaa wanaagsan + Ka sii wado daawashada daaqad yar oo appska kale dul istaagi doonta + Jiid si aad u dhaafiso + Eeg xogaha hoose 🐈 + Badhinka xajmiga daaraha + Fashil ka yimi dhiibaha + Koobu garee lifaaqa + Sharraxaad ma leh + Sharraxaadda + Duluc ma leh + Sawir-daaqadlaha + Habaynta Qrl-hoosaadka daaraha + Qrl-hoosaadka koromakaastiga + Hareeraha madow ka saar markaad daawanayso + Qoraal-hoosaadka + Habaynta Qrl-hoosaadka Koromakaastiga + Habka Xawaare-kordhinta + Waxaad kordhin karta xawaaraha aad ku daawanayso + Midig iyo bidix u jiid si aad marba dhinac ugu dhaafiso muqaalka + Laba jeer taabo midig ama bidix si aad u dhaafiso ama ku ceshato + Laba jeer taabo si aad u dhaafiso + Inta aad is dhaafinayso + Laba jeer taabo si aad u qallajiso + Dhexda taabo si aad u qallajisato + Gurmad u samee xogta + Iftiinka moobilka istcimaal + Cusbooneysii marba halkaad marayso daawashada + Tus goos-gooska + Waa fashilmay kaydinta gurmadku %s + Kasoo celi Xogta gurmadka aad kaydsatay + Dhererka K. kumeelgaadhka ah + Noocyada muqaal eed doorbiddo + Qaab TV + Heerka Kayd-yaraha + Masax kayd yaraha sawirrada/muqaalka + Laga yaaba inuu appku duqeeyo haddii aad badiso. + Laga yaabaa inuu wax xumeeyo haddii aad badiso, weliba aaladaha qaadka yar, sida Androyd tiifiiga. + Wuxuu muhiim u yahay inaad dhaafto Xannibaadaha ISPga + Ka saar fadhigan + Ku dar ka-reeb fadhi hore u jirey, laakiin hayb cusub wata + Aaraar + Haybta Seerfarka NGINX + Tusi Anemaha Turjuman/Leh Qrl-hoosaad + Lifaaqyada + Barta kaydka + Astaamaha + Badhinka X. la\'aanta + Qaybiyeyaasha + Iskii + Qaab moobil + Falal + Kayd-yare + Ishaarooyinka + Astaamaha daaraha + Guud + Tusi CEEBta qaybiyayaasha ku shaqeeya + Qaabka + Midab asaaska + Horidda Qoraal-hoosaadka + Nashqadda Appka + Goobta Cinwaanka boodhka + La degtay + Soo degaya + Ka Saar + Kor ama hoos u jiid labada dhinaca ee daaraha si aad u maamusho codka ama iftiinka + Iskii-u-daarka xalqadda xigta + Si iskii ah ayuu u daarayaa xalqadda xigta marka aad dhamaysato kaad daawanayso + Si iskii ah ha ula socdo appku hadba xalqadda aad marayso + Lifaaq lagama helin + Waad koobiyeysay lifaaqa + Daari xalqaddan + Xalqado ma leh + Wabsaydka + Filimaan + Taxanayaal + CEEB + Tirada inta ka muuqanaysa magaca filinka + Luuqadda bixiyaha + Qaabka Appka + Ka saar kuwa la daawaday + Eeg Kuwa Dadka kale Rakibeen + Codad kale + Far weyn ka dhig Qrl-hoosaadka + Sharraxaad + Xaaladda + Xajmiga + Safe mode ayaa daaran + Dhammaan Kordhiyeyaasha waa la wada demiyey cillad timi aawadeed si aad u ogaato ka cillada sababaya. + %s (La xayiray) + Marka dambe eed gasho isticmaal + Eeg warbixinra Cilladda + Qiimeynta: %s + Lahaanshaha + Qaybtii + HLS playlist + Ku shaqeeya + Luuqadda + Rakib kordhiyahan ugu horreyn + Muqaal daaraha aad doorbiddo + Masax taariikhda + Taariikhda + Furitaanka + Qoraalku wuu dheeryahay. Ma koobi garayn kartid. + Haa + Tusi badhin aad iska dhaafin karto bilowga/dhamaadka + Ku caalamadi in la daawaday + Dejinaya cusbooneysiinta appka… + Ma hubtaa inaad ka baxdid\? + May + PackageInstaller + Rakibaya cusbooneysiinta… + Waa la rakibi kari waayay qaybtan cusub ee appka + Lagama-maarmaanka + Marka aad ka baxdo baa ka cusbooneysiin doona appka + Dhig ciwaanka boodhka hoostiisa + Akoonka + Soo gal + Akoon geli + %s xaqiijisan + iiwarran@sxb.com + 127.0.0.1 + MagacKaYaabAh + Ka bax + Tusaale.com + Waxa lagu daray %s + Midna + Websaydkayga.com + Koodhka Luuqadda (en) + %s %s + Akoon samee + Talantaalli + Qiimaysan + Beddel akoonka + Ku dar raad-raace + /%d + %d / 10 + /\?\? + Waa lagu guuldarraystey inaad ku gasho %s + Caadi + Ugu weyn + Ugu yar + Hoos u degsan + Lakabka sare + Hagaaji Qrl-hosaadka + Isticmaal intan haddii Qrl-hoosaadku ay %d ms soo habsaanayaan + 4K + Dib u noqo + Hadhka + Kor-u-qaadan + Isticmaal intan haddii Qrl-hoosaadku ay %d ms soo hordhacayaan + 1000 ms + Ka guud + La jilaya + Sheekheenna wiilkiisa curad, gabadhiisu foox la\'aan ma heesi karto + HQ + Lagugula taliyey + Waxa laso kiciyey %s + Faylashaada ka keen + Internetka ka keen + Waa ka dejiyey faylka + Wajhadda dambe + Cam + HD + TS + SDR + Daaraha + Ciwaanka + Ka saar Qrl-hoosaadka asalka ah + Ka saar bararsanaanta Qrl-hoosaadka + Goor dhow… + Xigashada + Xulasho la\'aan + Cam + Cam + TC + Blu-ray + SD + WP + UHD + HDR + DVD + Sawirka Boodhka + Web + Hayb aan sax ahayn + Tayada iyo ciwaanka + Tayada + Aqoonsi aan sax ahayn + Xog ana sax ahayn + Xumaaday + Noocyada Muqaalka + Waxba kamaad bedelin daahitaanka Qrl-hoosaadka + U kala hor sida luuqadaha aad doorbidday + X.la\'aanta xigta + Sidkane + Sidkanayaasha + Saar kayd-weynaha + Dejiso liiska websaydyada aad dooneyso + Waxa la dejiyey: %d + Dejin xidhmeed + Waxa la saarayaa dhammaan sidkanayaasha kayd-weynaha + Xayiran: %d + Aan dejinayn: %d + Waxa la cusbooneysiiyey %d sidkane + Ugu horreyn cloudstream ma laha wax websaydyo uu filimaanta kasoo xigto, waxay noqonaysaa inaad adigu rakibato reboositarradooda... +\n +\nSababtuna waa in mar dhexdaas ah na dacweeyeen shirkadda Sky UK Limited🤮, markaa si aan mar dambe taasi u dhicin anagu kuma rakibi karno... +\n +\nDiscord naga soo qabo ama internetka ka baadh. + Soo deji dhamaan sidkanayaasha reboositarkan\? + Boodhka + Boodhka xalqadda + Boodhka weyn + Lambarkasirta123 + Dib-u-dhaca Qrl-hoosaadka + Dheeraadka + Goo-gooska + Lifaaqa daalacashada + Tixraacaha + Xiga + Ku daawo muuqaallada luuqadahan + Kii hore + Is dhaafi fadhiisintan + Beddel sida appku u muuqdo si uu ugu habboonaado moobilekaaga + Warbixinra cillad-saarka + Maxaad rabtaa inaad daawato + Dhan + Ku dar kayd-weyne + Magaca kayd-weynaha + Haybta kayd-weynaha + Waa la keenay sidkanaha + Waa ka dejiyey sidkanaha + Waa la tiray sidkanahan + Waa ka keeni kari waayey %s + 18+ + Waa la dejinayaa %d %s… + Waa la dejiyey %d %s + Dhammaan %s way dejisan yihiin + Kordhiyeyaasha + Liiska bulshada kale + Muqaal daaraha appka + VLC + MPV + Web Video Cast + Barawsarka + Kuuguma jiro appkaasi + Dhammaan luuqadaha + Is dhaafi %s + Dhamaadka + Soo koobidda + Dhamaad isku qasan + Bilowga + Bilow isku qasan + Qoraalka dhamaadka \ No newline at end of file diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 7e83f840..32336b66 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -2,7 +2,8 @@ Betygsatt: %.1f Hastighet (%.2fx) - Ny uppdatering hittad!\n%s -> %s + Ny uppdatering hittad! +\n%s -> %s CloudStream Hem Sök @@ -32,7 +33,7 @@ Undertexter Försök ansluta igen… Gå tillbaka - @string/result_poster_img_des + \@string/result_poster_img_des Spela Avsnitt Ladda ner Intern lagring @@ -43,7 +44,7 @@ Inaktivera automatisk felrapportering Mer information Hide - @string/result_poster_img_des + \@string/result_poster_img_des Spela upp Info Nästa @@ -124,7 +125,8 @@ A Ta bort nerladdad fil Ta bort - %s kommer att raderas permanent\nÄr du helt säker? + %s kommer att raderas permanent +\nÄr du helt säker\? Pågående Färdig Status diff --git a/app/src/main/res/values-tl/strings.xml b/app/src/main/res/values-tl/strings.xml index 12b4e96c..9e5b29d4 100644 --- a/app/src/main/res/values-tl/strings.xml +++ b/app/src/main/res/values-tl/strings.xml @@ -5,7 +5,7 @@ %s Ep %d Poster - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -15,7 +15,8 @@ Bilis (%.2fx) Rated: %.1f - Bagong update!\n%s -> %s + Bagong update! +\n%s -> %s CloudStream Home Maghanap @@ -145,7 +146,8 @@ Tanggalin I-pause I-resume - This will permanently delete %s\nAre you sure? + This will permanently delete %s +\nAre you sure\? Patuloy Tapos na Katayuan diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 2e3bddc6..03eee72b 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -13,25 +13,26 @@ %d %s Ep %d Cast: %s - Bölüm %d şu tarihte yayınlanacak: + Bölüm %d şu tarihte yayınlanacak: %dd %dh %dm %dh %dm %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Bölüm Posteri Ana Poster Sonraki Rastgele - @string/play_episode + \@string/play_episode Geri git - @string/home_change_provider_img_des + \@string/home_change_provider_img_des Change Provider Preview Background Hız (%.2fx) Puan: %.1f - Yeni güncelleme bulundu!\n%s -> %s + Yeni güncelleme bulundu! +\n%s -> %s Filler %d dakika CloudStream @@ -45,7 +46,7 @@ Veri yok Daha fazla seçenek Sonraki bölüm - @string/synopsis + \@string/synopsis Türler Paylaş Tarayıcıda aç @@ -122,7 +123,7 @@ İzlemeye devam et Kaldır Daha fazla bilgi - @string/home_play + \@string/home_play Bu sağlayıcının düzgün çalışması için bir VPN gerekebilir Bu sağlayıcı bir torrent. VPN önerilir Metadata site tarafından sağlanmamış, veri site\'de bulunmuyorsa video yüklenmesi başarısız olacak. @@ -204,13 +205,15 @@ Bölüm bulunamadı Dosyayı sil Sil - @string/sort_cancel + \@string/sort_cancel Durdur Sürdür -30 +30 - %s dosyası tamamen silinecek\nEmins misiniz? - %dm\nkaldı + %s dosyası tamamen silinecek +\nEmins misiniz\? + %dm +\nkaldı Devam ediyor Tamamlandı Durum @@ -241,8 +244,8 @@ Film Dizi Çizgi film - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Belgesel Asya draması @@ -257,7 +260,7 @@ Bölümü Chromecast ile yayınla Bağlantıyı Chromecast ile yayınla Uygulamada oynat - %s\'de\/da oynat + %s\'deda oynat Tarayıcıda oynat Linki kopyala Otomatik indir @@ -304,22 +307,7 @@ Yakınlaştır Disclaimer legal_notice_key - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. Genel Rastgele butonu Ana sayfada rastgele butonunu göster @@ -370,7 +358,7 @@ Senkronize et Puan %d / 10 - /?? + /\?\? /%d %s için doğrulama başarılı %s için doğrulama başarısız @@ -380,7 +368,7 @@ Hepsi Maksimum Minimum - @string/none + \@string/none Dış hat Çökmüş Gölge @@ -447,7 +435,7 @@ Kurulumu atla Cihazınıza uygun görünümü seçin Çökme raporları - Ne izlemek istiyorsunuz? + Ne izlemek istiyorsunuz\? Bitti Eklentiler Depo ekle @@ -474,7 +462,7 @@ Topluluk depolarını görüntüle Herkese açık liste Tüm alt yazılar büyük harf - Bu depodaki tüm eklentiler indirilsin mi? + Bu depodaki tüm eklentiler indirilsin mi\? %s devre dışı bırakıldı Parçalar Ses parçaları @@ -501,7 +489,7 @@ İnternet tarayıcısı Uygulama bulunamadı Geçmiş - İzlendi olarak imle + İzlendi olarak işaretle Uygulama güncellemesi yükleniyor… Kurulum işlemini tekrarla Fragmanı oynat @@ -529,11 +517,12 @@ Çıkmak istediğine emin misin\? Evet Uygulama güncellemesi indiriliyor… - Uygulamanın yeni sürümü yüklenemedi. + Uygulamanın yeni sürümü yüklenemedi Geçmişi temizle Paket yükleyici Eski Hareketler Tüm diller Geç %s + İzlenenlerden kaldır \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d139144e..93e51c84 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -94,9 +94,9 @@ Філер Програти в CloudStream Потік - Переглянуто + Переглядаю Поділитися - Призупинено + Відкладено Повторний перегляд Завантажити Відтворити трансляцію diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 64b2c0a5..8b70569a 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -10,7 +10,7 @@ %dm Poster - @string/result_poster_img_des + \@string/result_poster_img_des Episode Poster Main Poster Next Random @@ -20,7 +20,8 @@ Tốc độ (%.2fx) Đánh giá: %.1f - Đã có bản cập nhật mới!\n%s -> %s + Đã có bản cập nhật mới! +\n%s -> %s Bộ lọc %d phút CloudStream @@ -109,7 +110,7 @@ Tiếp tục xem Loại bỏ Thông tin thêm - @string/home_play + \@string/home_play Bạn có thể sẽ cần sử dụng VPN để xem phim này Phim này được chiếu dưới dạng Torrent. Hãy sử dụng VPN để xem Siêu dữ liệu không được cung cấp bởi trang web, quá trình tải video sẽ không thành công nếu nó không tồn tại trên trang web. @@ -147,7 +148,7 @@ Đã tải dữ liệu sao lưu Không thể khôi phục dữ liệu từ %s Khôi phục dữ liệu thành công - Thiếu quyền truy cập bộ nhớ, hãy thử lại + Thiếu quyền truy cập bộ nhớ, hãy thử lại. Lỗi khi sao lưu %s Tìm kiếm Tài khoản @@ -160,7 +161,7 @@ Hiển thị tập phụ cho anime Hiển thị trailer Hiển thị poster từ kitsu - Ẩn chất lượng Video khi tìm kiếm + Ẩn chất lượng video khi tìm kiếm Tự động cập nhật plugin Hiển thị thông báo cập nhật App Tự động tìm kiếm và thông báo khi có bản cập nhật mới @@ -194,8 +195,10 @@ Tiếp Tục -30 +30 - %s sẽ bị xoá vĩnh viễn\nBạn có chắc chắn muốn xóa? - %dm\ncòn lại + %s sẽ bị xoá vĩnh viễn +\nBạn có chắc chắn muốn xóa\? + %dm +\ncòn lại Đang Chiếu Hoàn Thành Trạng Thái @@ -226,8 +229,8 @@ Phim Lẻ Phim Bộ Hoạt Hình - @string/anime - @string/ova + \@string/anime + \@string/ova Torrent Phim Tài Liệu Truyền Hình Châu Á @@ -322,7 +325,7 @@ Đồng bộ Đánh giá %d / 10 - /?? + /\?\? /%d Đã xác thực %s Không thể xác thực %s @@ -424,7 +427,7 @@ Xem kho lưu trữ của cộng đồng Danh sách công khai In hoa toàn bộ phụ đề - Tải toàn bộ plugin từ kho lưu trữ này? + Tải toàn bộ plugin từ kho lưu trữ này\? %s (Đã vô hiệu) Thêm Âm thanh @@ -435,4 +438,13 @@ Xem thông tin sự cố Lịch sử Đánh dấu là đã xem + Tự động tải plugin + Thiết lập lại + Bộ cài APK + Một số máy không hỗ trợ trình cài đặt gói mới. Hãy thử tùy chọn cũ nếu các bản cập nhật không cài đặt. + %s %d%s + Xem Trailer + Tự động tải plugins còn thiếu. + Bắt đầu cập nhật + Liên kết \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 764304d7..a8341d46 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -19,19 +19,20 @@ %dm 封面 - @string/result_poster_img_des + \@string/result_poster_img_des 劇集封面 主封面 隨機下一個 - @string/play_episode + \@string/play_episode 返回 - @string/home_change_provider_img_des + \@string/home_change_provider_img_des 更改片源 預覽背景 速度(%.2fx) 評分:%.1f - 發現新版本!\n%s -> %s + 發現新版本! +\n%s -> %s 填充 %d 分鐘 CloudStream @@ -45,7 +46,7 @@ 無資料 更多選項 下一集 - @string/synopsis + \@string/synopsis 類型 分享 在瀏覽器中打開 @@ -122,7 +123,7 @@ 繼續觀看 移除 更多資訊 - @string/home_play + \@string/home_play 此片源可能需要 VPN 才能正常使用 此片源是種子,建議使用 VPN 站點不提供元數據,如果站點上不存在元數據,影片載入將失敗。 @@ -149,8 +150,7 @@ 輕按兩下以控制進度 輕按兩下以暫停 輕按兩下以控制進度時間 - 在右側或左側輕按兩次以向前或向後快轉 - + 在右側或左側輕按兩次以向前或向後快轉 輕按兩下中間以暫停 使用系統亮度 在應用程序播放器中使用系統亮度替代黑色遮罩 @@ -205,12 +205,13 @@ 未找到劇集 刪除文件 刪除 - @string/sort_cancel + \@string/sort_cancel 暫停 繼續 -30 +30 - 這將永遠刪除 %s\n你確定嗎? + 這將永遠刪除 %s +\n你確定嗎\? %d 分鐘 \n剩餘的 連載中 @@ -243,8 +244,8 @@ 電影 電視劇 卡通 - @string/anime - @string/ova + \@string/anime + \@string/ova 種子 紀錄片 亞洲劇 @@ -306,22 +307,7 @@ 縮放 免責聲明 legal_notice_key - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. 通用 隨機按鈕 在主頁中顯示隨機按鈕 @@ -372,7 +358,7 @@ 同步 評分 %d / 10 - /?? + /\?\? /%d %s 已驗證 無法在 %s 登入 @@ -382,7 +368,7 @@ 全部 最大 最小 - @string/none + \@string/none 輪廓 凹陷 陰影 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9545956e..97a48597 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -19,19 +19,20 @@ %dm 封面 - @string/result_poster_img_des + \@string/result_poster_img_des 剧集封面 主封面 随机下一个 - @string/play_episode + \@string/play_episode 返回 - @string/home_change_provider_img_des + \@string/home_change_provider_img_des 更改片源 预览背景 速度(%.2fx) 评分:%.1f - 发现新版本!\n%s -> %s + 发现新版本! +\n%s -> %s 填充 %d 分钟 CloudStream @@ -45,7 +46,7 @@ 无数据 更多选项 下一集 - @string/synopsis + \@string/synopsis 类型 分享 在浏览器中打开 @@ -122,7 +123,7 @@ 继续观看 移除 更多信息 - @string/home_play + \@string/home_play 此片源可能需要 VPN 才能正常使用 此片源为种子文件,建议使用 VPN 站点不提供元数据,如果站点上不存在元数据,视频加载将失败。 @@ -149,12 +150,10 @@ 双击控制进度 双击暂停 双击控制进度时间 - 在左右侧双击快进或快退 - + 在左右侧双击快进或快退 双击中间暂停 使用系统亮度 - 在应用播放器中使用系统亮度替代黑色遮罩 - + 在应用播放器中使用系统亮度替代黑色遮罩 更新观看进度 自动同步当前剧集进度 从备份中恢复数据 @@ -194,8 +193,7 @@ 链接已复制到剪贴板 播放剧集 重置为默认值 - 抱歉,应用崩溃了,将发送一份匿名错误报告给开发者 - + 抱歉,应用崩溃了,将发送一份匿名错误报告给开发者 %s %d%s 无季 @@ -208,12 +206,13 @@ 未找到剧集 删除文件 删除 - @string/sort_cancel + \@string/sort_cancel 暂停 继续 -30 +30 - 这将永久删除 %s\n您确定吗? + 这将永久删除 %s +\n您确定吗\? %d 分钟 \n剩余 连载中 @@ -246,8 +245,8 @@ 电影 电视剧 卡通 - @string/anime - @string/ova + \@string/anime + \@string/ova 种子 纪录片 亚洲剧 @@ -309,22 +308,7 @@ 缩放 免责声明 legal_notice_key - Any legal issues regarding the content on this application - should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. - - In case of copyright infringement, please directly contact the responsible parties or the streaming websites. - - The app is purely for educational and personal use. - - CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. - CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or - manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, - user-friendly interface. - - It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the - responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use - CloudStream 3 at your own risk. - + Any legal issues regarding the content on this application should be taken up with the actual file hosts and providers themselves as we are not affiliated with them. In case of copyright infringement, please directly contact the responsible parties or the streaming websites. The app is purely for educational and personal use. CloudStream 3 does not host any content on the app, and has no control over what media is put up or taken down. CloudStream 3 functions like any other search engine, such as Google. CloudStream 3 does not host, upload or manage any videos, films or content. It simply crawls, aggregates and displayes links in a convenient, user-friendly interface. It merely scrapes 3rd-party websites that are publicly accessable via any regular web browser. It is the responsibility of user to avoid any actions that might violate the laws governing his/her locality. Use CloudStream 3 at your own risk. 通用 随机按钮 在主页中显示随机按钮 @@ -375,7 +359,7 @@ 同步 评分 %d / 10 - /?? + /\?\? /%d 已验证 %s 无法登录到 %s @@ -385,7 +369,7 @@ 全部 最大 最小 - @string/none + \@string/none 轮廓 凹陷 阴影 @@ -552,4 +536,5 @@ 更新开始 应用退出后将会更新 插件已下载 + 从已观看中移除 \ No newline at end of file From 5050ff65c06a8cb1bdef74561ee9ef3ae77f1ff9 Mon Sep 17 00:00:00 2001 From: reduplicated <110570621+reduplicated@users.noreply.github.com> Date: Wed, 25 Jan 2023 15:06:48 +0100 Subject: [PATCH 11/47] disabled crash reporting because yall keep crashing --- app/build.gradle.kts | 4 +- .../lagradost/cloudstream3/AcraApplication.kt | 4 +- .../com/lagradost/cloudstream3/MainAPI.kt | 55 ++++++++++++++++++- .../ui/setup/SetupFragmentLayout.kt | 2 +- app/src/main/res/xml/settings_updates.xml | 2 +- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f8e0091c..1cbcec68 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 33 - versionCode = 55 - versionName = "3.4.0" + versionCode = 56 + versionName = "3.5.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") diff --git a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt index 198f0f4c..0351b1ff 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/AcraApplication.kt @@ -43,9 +43,9 @@ class CustomReportSender : ReportSender { override fun send(context: Context, errorContent: CrashReportData) { println("Sending report") val url = - "https://docs.google.com/forms/u/0/d/e/1FAIpQLSe9Vff8oHGMRXcjgCXZwkjvx3eBdNpn4DzjO0FkcWEU1gEQpA/formResponse" + "https://docs.google.com/forms/d/e/1FAIpQLSdOlbgCx7NeaxjvEGyEQlqdh2nCvwjm2vwpP1VwW7REj9Ri3Q/formResponse" val data = mapOf( - "entry.1586460852" to errorContent.toJSON() + "entry.753293084" to errorContent.toJSON() ) thread { // to not run it on main thread diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 6565b144..8c818027 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -14,7 +14,7 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.ui.player.SubtitleData -import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf import com.lagradost.cloudstream3.utils.ExtractorLink @@ -81,7 +81,7 @@ object APIHolder { synchronized(allProviders) { initMap() return apiMap?.get(apiName)?.let { apis.getOrNull(it) } - // Leave the ?. null check, it can crash regardless + // Leave the ?. null check, it can crash regardless ?: allProviders.firstOrNull { it?.name == apiName } } } @@ -318,6 +318,57 @@ object APIHolder { } } +/* +// THIS IS WORK IN PROGRESS API +interface ITag { + val name: UiText +} + +data class SimpleTag(override val name: UiText, val data: String) : ITag + +enum class SelectType { + SingleSelect, + MultiSelect, + MultiSelectAndExclude, +} + +enum class SelectValue { + Selected, + Excluded, +} + +interface GenreSelector { + val title: UiText + val id : Int +} + +data class TagSelector( + override val title: UiText, + override val id : Int, + val tags: Set, + val defaultTags : Set = setOf(), + val selectType: SelectType = SelectType.SingleSelect, +) : GenreSelector + +data class BoolSelector( + override val title: UiText, + override val id : Int, + + val defaultValue : Boolean = false, +) : GenreSelector + +data class InputField( + override val title: UiText, + override val id : Int, + + val hint : UiText? = null, +) : GenreSelector + +// This response describes how a user might filter the homepage or search results +data class GenreResponse( + val searchSelectors : List, + val filterSelectors: List = searchSelectors +) */ /* 0 = Site not good diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt index bc9bfb1f..50fb37d6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/setup/SetupFragmentLayout.kt @@ -67,7 +67,7 @@ class SetupFragmentLayout : Fragment() { crash_reporting_text?.text = getText(text) } - val enableCrashReporting = !settingsManager.getBoolean(ACRA.PREF_DISABLE_ACRA, false) + val enableCrashReporting = !settingsManager.getBoolean(ACRA.PREF_DISABLE_ACRA, true) acra_switch.isChecked = enableCrashReporting crash_reporting_text.text = getText( diff --git a/app/src/main/res/xml/settings_updates.xml b/app/src/main/res/xml/settings_updates.xml index ba098c8c..f2ec6747 100644 --- a/app/src/main/res/xml/settings_updates.xml +++ b/app/src/main/res/xml/settings_updates.xml @@ -2,7 +2,7 @@ Date: Thu, 26 Jan 2023 00:34:55 +0100 Subject: [PATCH 12/47] webview crash fix --- .../com/lagradost/cloudstream3/network/CloudflareKiller.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt index 068cb968..6950d961 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt @@ -64,7 +64,9 @@ class CloudflareKiller : Interceptor { } private fun getWebViewCookie(url: String): String? { - return CookieManager.getInstance()?.getCookie(url) + return normalSafeApiCall { + CookieManager.getInstance()?.getCookie(url) + } } /** From b2b16fccc529e9bd44ed4c2267f41fb3e72bb0c6 Mon Sep 17 00:00:00 2001 From: LiJu09 <40698774+LiJu09@users.noreply.github.com> Date: Fri, 27 Jan 2023 23:44:00 +0000 Subject: [PATCH 13/47] [extractor] added ByteShare (#337) * add byteshare extractor * reformat code * make it simple * no regex --- .../cloudstream3/extractors/ByteShare.kt | 23 +++++++++++++++++++ .../cloudstream3/utils/ExtractorApi.kt | 1 + 2 files changed, 24 insertions(+) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/ByteShare.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/ByteShare.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/ByteShare.kt new file mode 100644 index 00000000..3e0a03c0 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/ByteShare.kt @@ -0,0 +1,23 @@ +package com.lagradost.cloudstream3.extractors + +import com.lagradost.cloudstream3.utils.* + +open class ByteShare : ExtractorApi() { + override val name = "ByteShare" + override val mainUrl = "https://byteshare.net" + override val requiresReferer = false + + override suspend fun getUrl(url: String, referer: String?): List { + val sources = mutableListOf() + sources.add( + ExtractorLink( + name, + name, + url.replace("/embed/", "/download/"), + "", + Qualities.Unknown.value, + ) + ) + return sources + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index 73603964..bd4f8705 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -361,6 +361,7 @@ val extractorApis: MutableList = arrayListOf( Cda(), Dailymotion(), + ByteShare(), ) From b2389bf14cfcedd17b9a5c73c22d25e985530d62 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 19:00:10 +0100 Subject: [PATCH 14/47] Add padding in info & download to remove obtrusion by FAB --- app/src/main/res/layout/fragment_downloads.xml | 4 ++-- app/src/main/res/layout/fragment_result.xml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/fragment_downloads.xml b/app/src/main/res/layout/fragment_downloads.xml index f88e39d0..65f36209 100644 --- a/app/src/main/res/layout/fragment_downloads.xml +++ b/app/src/main/res/layout/fragment_downloads.xml @@ -5,7 +5,6 @@ android:id="@+id/download_root" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/primaryGrayBackground" android:orientation="vertical" tools:context=".ui.download.DownloadFragment"> @@ -132,7 +131,8 @@ + android:paddingBottom="100dp" + android:clipToPadding="false" + android:layout_height="wrap_content"> Date: Sat, 28 Jan 2023 19:39:12 +0100 Subject: [PATCH 15/47] Made player_video_title_rez disappear if blank --- .../ui/player/FullScreenPlayer.kt | 2 +- .../cloudstream3/ui/player/GeneratorPlayer.kt | 4 +- .../main/res/layout/player_custom_layout.xml | 53 ++++++++++--------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index c79cdd76..2dad05ef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -605,7 +605,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { player_top_holder?.isGone = isGone //player_episodes_button?.isVisible = !isGone && hasEpisodes player_video_title?.isGone = togglePlayerTitleGone - player_video_title_rez?.isGone = isGone +// player_video_title_rez?.isGone = isGone player_episode_filler?.isGone = isGone player_center_menu?.isGone = isGone player_lock?.isGone = !isShowing diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index bf39edc7..e15dcee6 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -1149,13 +1149,15 @@ class GeneratorPlayer : FullScreenPlayer() { val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name ?: "NULL" - player_video_title_rez?.text = when (titleRez) { + val title = when (titleRez) { 0 -> "" 1 -> extra 2 -> source 3 -> "$source - $extra" else -> "" } + player_video_title_rez?.text = title + player_video_title_rez?.isVisible = title.isNotBlank() } override fun playerDimensionsLoaded(widthHeight: Pair) { diff --git a/app/src/main/res/layout/player_custom_layout.xml b/app/src/main/res/layout/player_custom_layout.xml index 691795d3..683a1077 100644 --- a/app/src/main/res/layout/player_custom_layout.xml +++ b/app/src/main/res/layout/player_custom_layout.xml @@ -96,33 +96,36 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + @@ -319,23 +322,23 @@ + tools:text="Skip Opening" + tools:visibility="visible" /> Date: Sat, 28 Jan 2023 05:23:06 +0100 Subject: [PATCH 16/47] Translated using Weblate (Russian) Currently translated at 88.0% (499 of 567 strings) Translated using Weblate (Russian) Currently translated at 86.7% (492 of 567 strings) Translated using Weblate (Romanian) Currently translated at 75.6% (429 of 567 strings) Update translation files Updated by "Remove blank strings" hook in Weblate. Translated using Weblate (Russian) Currently translated at 86.5% (491 of 567 strings) Translated using Weblate (Vietnamese) Currently translated at 99.2% (563 of 567 strings) Translated using Weblate (Slovak) Currently translated at 33.1% (188 of 567 strings) Translated using Weblate (Somali) Currently translated at 99.6% (565 of 567 strings) Translated using Weblate (Russian) Currently translated at 84.1% (477 of 567 strings) Translated using Weblate (German) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Russian) Currently translated at 84.1% (477 of 567 strings) Translated using Weblate (Russian) Currently translated at 84.1% (477 of 567 strings) Translated using Weblate (Turkish) Currently translated at 100.0% (567 of 567 strings) Translated using Weblate (Bulgarian) Currently translated at 100.0% (567 of 567 strings) Added translation using Weblate (Slovak) Translated using Weblate (Vietnamese) Currently translated at 99.4% (564 of 567 strings) Co-authored-by: Alexey Co-authored-by: Hosted Weblate Co-authored-by: John Doe Co-authored-by: Juraj Liso Co-authored-by: Shafici Isxariifshe Co-authored-by: alex Co-authored-by: eightyy8 Co-authored-by: kaajjo Co-authored-by: tuan041 <30403510+tuan041@users.noreply.github.com> Co-authored-by: tuan041 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/bg/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ro/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/sk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/so/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/tr/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translation: Cloudstream/App --- app/src/main/res/values-bg/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-ro/strings.xml | 6 +- app/src/main/res/values-ru/strings.xml | 38 +++++++-- app/src/main/res/values-sk/strings.xml | 107 +++++++++++++++++++++++++ app/src/main/res/values-so/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 24 ++++-- app/src/main/res/values-vi/strings.xml | 51 +++++++++++- 8 files changed, 214 insertions(+), 17 deletions(-) create mode 100644 app/src/main/res/values-sk/strings.xml diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index 324f44bc..9f95eb3f 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -497,4 +497,5 @@ Приставката е изтеглена Приложението ще се актуализира при изход от него Започна Актуализация + Премахване от гледани \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 33afd571..00aa5b97 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -17,7 +17,7 @@ Intro Verlauf löschen Verlauf - Überspringen Button für Openings/Endings anzeigen + Überspringen Knopf für Openings/Endings anzeigen Zu viel Text. Kann nicht in der Zwischenablage gespeichert werden. Episodenvorschaubild Medienvorschaubild diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 28673fe2..982546bc 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -141,7 +141,7 @@ Copie de rezervă a datelor Fișier de rezervă încărcat Imposibilitatea de a restaura datele din %s - Datele au fost salvate cu succes + Date stocate Permisiuni de arhivare lipsă, vă rugăm să încercați din nou Eroare de backup %s Căutare @@ -380,4 +380,8 @@ CloudStream Vizionează trailerul Actualizarea a început + Actualizați progresul ceasului + Începe următorul episod când se termină episodul curent + Ascundeți calitatea video selectată în rezultatele căutării + Redare Livestream \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 930437c7..39e74794 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -12,7 +12,7 @@ Скачать неудачный Подогнать Удалить - Всё + Все Пауза Актёрский состав: %s Название источника @@ -53,7 +53,7 @@ Поделиться Открыть в браузер Пропустить загрузку - Смотрю + Просмотр Приостановленно Завершено Брошенный @@ -81,8 +81,8 @@ Подробнее Фильтр закладки Закладки - Наносить - Прервать + Применить + Отмена Копия Закрыть Очистить @@ -279,7 +279,7 @@ Используйте яркость системы в проигрывателе приложения вместо темного наложения Обновить состояние хода просмотра Данные сохранены - Дает вам результаты поиска, разделенные по провайдеру + Показывает результаты поиска, разделенные по провайдеру Поиск предварительных обновлений вместо полных выпусков Повторить процесс настройки Этот провайдер не поддерживает Chromecast @@ -350,7 +350,7 @@ Добавить учётную запись МойКрутойСайт example.com - Язык (en) + Код языка (ru) учётная запись Автоматически 127.0.0.1 @@ -394,7 +394,7 @@ %s %s %s аутентифицировано Не удалось перейти к %s - Максимум + Макс Минимум Очертание Тень @@ -408,4 +408,28 @@ Съешь ещё этих мягких французских булок, да выпей же чаю Рекомендуется Загружено %s + \@нить/аниме + \@нить/ova + Этикетка Dub + Сайт + Функции + Главное + Источник + Случайный + Скоро будет… + Этикетка Sub + Фон + Вид + Трейлер + %s (отключено) + Следующий + В CloudStream по умолчанию не установлены сайты. Вам необходимо установить сайты из репозиториев. +\n +\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем связать сайт репозитория в приложении. +\n +\nПрисоединяйтесь к нашему Discord или ищите в интернете. + Недопустимые данные + Разрешение и название + Предыдущий + Разрешение \ No newline at end of file diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml new file mode 100644 index 00000000..97039233 --- /dev/null +++ b/app/src/main/res/values-sk/strings.xml @@ -0,0 +1,107 @@ + + + Našla sa nová aktualizácia! +\n%s -> %s + Výplň + %dh %dm + Epizóda %d bude vydaná za + %s Ep %d + Ďalšia epizóda + Žánre + Zdielať + Otvoriť v prehliadači + Preskočiť načítavanie + Hrajú: %s + CloudStream + Plagát + %dd %dh %dm + %dm + %d min + \@string/result_poster_img_des + Plagát epizódy + Hlavný plagát + Prehrať s CloudStream + Nastavenia + Hľadať %s… + Pokračovať v sťahovaní + Hodnotenie: %.1f + Ísť späť + Rýchlosť (%.2fx) + Zmeniť poskytovateľa + Domov + Hľadať + Hľadať… + Sťahovanie + Žiadne dáta + Zrušiť + Kopírovať + Zavrieť + Uložiť + Stiahnuť + Stiahnuté + Ďalšie možnosti + Zdroje + Ísť späť + Sťahovanie zlyhalo + Sťahovanie pozastavené + Sťahovanie dokončené + Chyba pri načítavaní odkazov + Aktualizácia spustená + Interné úložisko + Načítavanie… + Dokončené + Plánujem pozerať + Zakázať automatické nahlasovanie chýb + Viac informácií + Záložky + Prehrať film + Prehrať upútavku + Sťahovanie + Sťahovanie zrušené + Dab + Zmazať súbor + Žiadny + Tit + Opätovné sledovanie + Prehrať súbor + Info + Prehrať živý prenos + Titulky + Prehrať epizódu + Pozastaviť sťahovanie + Skryť + Filtrovať záložky + Odstrániť + Použiť + Sťahovanie spustené + Vyčistiť + Prehrať + Nastaviť stav sledovania + Rýchlosť prehrávania + Farba obrysu + Farba okna + Typ hrany + Nastavenia titulkov + Farba pozadia + Farba textu + Vyvýšenie titulkov + Hľadať pomocou poskytovateľov + Písmo + Hľadať pomocou typov + Automaticky vybrať jazyk + Jazyk titulkov + Veľkosť písma + Nedarovali ste žiadne benény + Podržaním obnovíte predvolené nastavenia + %d benénov darovaných vývojárom + Stiahnuť jazyky + Odstrániť + Tento poskytovateľ je torrent, odporúča sa VPN + Importovať písma ich umiestnením do %s + Viac informácií + \@string/home_play + Pokračovať v sledovaní + Na správne fungovanie tohto poskytovateľa môže byť potrebná VPN + Stránka neposkytla žiadne metadáta, načítanie videa zlyhá, ak na stránke neexistuje. + Popis + \ No newline at end of file diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml index 8bb22e66..b944b6b3 100644 --- a/app/src/main/res/values-so/strings.xml +++ b/app/src/main/res/values-so/strings.xml @@ -4,7 +4,7 @@ %dm %ds %dd %ds %dd %dd - %s Ep %d + %s Xlq %d Xalqadda %d waxa lasoo deyn doonaa Daji Dejintii ma guulaysan diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 03eee72b..bd245a61 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -13,7 +13,7 @@ %d %s Ep %d Cast: %s - Bölüm %d şu tarihte yayınlanacak: + Bölüm %d şu tarihte yayınlanacak %dd %dh %dm %dh %dm %dm @@ -435,18 +435,18 @@ Kurulumu atla Cihazınıza uygun görünümü seçin Çökme raporları - Ne izlemek istiyorsunuz\? + Ne izlemek istiyorsunuz Bitti Eklentiler Depo ekle Depo ismi - Depo URL\'i + Depo URL\'si Eklenti yüklendi Eklenti silindi %s yüklenemedi +18 - %d %s indirilmeye başlandı - %d %s başarıyla indirildi + %d %s … indirilmeye başlandı + %d %s indirildi %s\'nin tamamı zaten indirildi Toplu indir eklenti @@ -458,7 +458,11 @@ Devre dışı: %d İndirilmeyen: %d %d eklenti(ler) güncellendi - Site eklentilerini yüklemek için bir depo ekleyin + CloudStream\'in varsayılan olarak yüklü sitesi yoktur. Siteleri depolardan kurmanız gerekir. +\n +\nSky UK Limited tarafından beyinsiz bir DMCA yayından kaldırma 🤮 nedeniyle uygulama içinde depo linklerini bulunduramıyoruz. +\n +\nDiscord\'umuza katılın veya çevrimiçi arama yapın. Topluluk depolarını görüntüle Herkese açık liste Tüm alt yazılar büyük harf @@ -525,4 +529,12 @@ Tüm diller Geç %s İzlenenlerden kaldır + Karışık son + Karışık başlangıç + Kredi + Giriş + Eklenti İndirildi + Aksiyonlar + Açma/bitiş için atlama açılır pencerelerini göster + Çok fazla metin. Panoya kaydedilemiyor. \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 8b70569a..3aa5cf69 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -113,7 +113,6 @@ \@string/home_play Bạn có thể sẽ cần sử dụng VPN để xem phim này Phim này được chiếu dưới dạng Torrent. Hãy sử dụng VPN để xem - Siêu dữ liệu không được cung cấp bởi trang web, quá trình tải video sẽ không thành công nếu nó không tồn tại trên trang web. Thông tin phim Đang cập nhật Không tìm thấy thông tin @@ -447,4 +446,54 @@ Tự động tải plugins còn thiếu. Bắt đầu cập nhật Liên kết + Danh sách HLS + Trình phát ưu tiên + Trình phát mặc địng + Đánh giá: %s + Không + Phiên bản + Tác giả + Cập nhật ứng dụng + Sao lưu + Tiện ích + Hành động + Cache + Cử chỉ + Tính năng trình phát + Phụ đề + Bố cục + Mặc định + Giao diện + Tính năng + Đã cập nhật %d plugin + Mô tả + Trạng thái + Kích thước + Hỗ trợ + Ngôn ngữ + Cài đặt tiện ích trước + VLC + MPV + Web Video Cast + Trình duyệt web + Không thấy ứng dụng + Tất cả ngôn ngữ + Tua %s + Mở đầu + Kết thúc + Tóm tắt + Mở đầu tuỳ chọn + Kết thúc tuỳ chọn + Danh đề + Giới thiệu + Xoá lịch sử + Hiển thị cửa sổ tua cho mở đầu/kết thúc + Văn bản quá dài. Không thể lưu vào bộ nhớ tạm. + Xoá khỏi đã xem + Bạn có chắc muốn thoát\? + + Đang tải bản cập nhật… + Đang cài bản cập nhật… + Không thể cài đặt phiên bản mới + Ứng dụng sẽ được cập nhật khi thoát \ No newline at end of file From 9d11dc76a104bdd0aaff06335955b3083aed076e Mon Sep 17 00:00:00 2001 From: "recloudstream[bot]" <111277985+recloudstream[bot]@users.noreply.github.com> Date: Sat, 28 Jan 2023 18:43:31 +0000 Subject: [PATCH 17/47] update list of locales --- .../com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt index 1dcaf350..3f1c781a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsGeneral.kt @@ -85,6 +85,7 @@ val appLanguages = arrayListOf( Triple("\uD83C\uDDF5\uD83C\uDDF9", "Portuguese", "pt"), Triple("", "Romanian", "ro"), Triple("", "Russian", "ru"), + Triple("", "Slovak", "sk"), Triple("", "Somali", "so"), Triple("", "Swedish", "sv"), Triple("", "Tamil", "ta"), From 3c82548c20a1ba523e8a5a76e4734c1620565bfb Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:38:02 +0000 Subject: [PATCH 18/47] Library merge (#343) --- app/build.gradle.kts | 3 + .../com/lagradost/cloudstream3/MainAPI.kt | 25 ++ .../lagradost/cloudstream3/MainActivity.kt | 6 + .../metaproviders/AnilistRedirector.kt | 30 -- .../metaproviders/SyncRedirector.kt | 56 +++ .../syncproviders/AccountManager.kt | 3 +- .../cloudstream3/syncproviders/SyncAPI.kt | 88 +++- .../cloudstream3/syncproviders/SyncRepo.kt | 22 +- .../syncproviders/providers/AniListApi.kt | 133 ++++-- .../syncproviders/providers/LocalList.kt | 100 +++++ .../syncproviders/providers/MALApi.kt | 135 ++++-- .../cloudstream3/ui/AutofitRecyclerView.kt | 5 +- .../cloudstream3/ui/home/HomeFragment.kt | 4 +- .../ui/library/LibraryFragment.kt | 393 ++++++++++++++++++ .../ui/library/LibraryScrollTransformer.kt | 17 + .../ui/library/LibraryViewModel.kt | 104 +++++ .../ui/library/LoadingPosterAdapter.kt | 37 ++ .../cloudstream3/ui/library/PageAdapter.kt | 130 ++++++ .../ui/library/ViewpagerAdapter.kt | 90 ++++ .../ui/quicksearch/QuickSearchFragment.kt | 2 +- .../cloudstream3/ui/result/ResultFragment.kt | 8 +- .../ui/result/ResultFragmentPhone.kt | 2 +- .../ui/result/ResultFragmentTv.kt | 2 +- .../ui/result/ResultViewModel2.kt | 21 +- .../cloudstream3/ui/search/SearchAdaptor.kt | 4 + .../cloudstream3/ui/search/SearchFragment.kt | 2 +- .../ui/search/SearchResultBuilder.kt | 5 +- .../ui/settings/extensions/PluginsFragment.kt | 2 +- .../lagradost/cloudstream3/utils/AppUtils.kt | 55 +++ .../cloudstream3/utils/BackupUtils.kt | 4 - .../cloudstream3/utils/DataStoreHelper.kt | 22 +- .../lagradost/cloudstream3/utils/SyncUtil.kt | 27 +- .../lagradost/cloudstream3/utils/UIHelper.kt | 54 ++- app/src/main/res/color/item_select_color.xml | 2 + .../ic_baseline_collections_bookmark_24.xml | 6 + .../main/res/drawable/ic_baseline_sort_24.xml | 5 + .../main/res/drawable/ic_baseline_star_24.xml | 4 +- .../drawable/ic_outline_account_circle_24.xml | 6 + .../res/drawable/indicator_background.xml | 6 + app/src/main/res/drawable/rating_bg_color.xml | 6 + app/src/main/res/layout/activity_main.xml | 4 +- app/src/main/res/layout/fragment_library.xml | 177 ++++++++ .../res/layout/library_viewpager_page.xml | 11 + .../res/layout/loading_poster_dynamic.xml | 37 ++ .../layout/search_result_grid_expanded.xml | 131 ++++-- app/src/main/res/menu/bottom_nav_menu.xml | 29 +- app/src/main/res/menu/library_menu.xml | 17 + .../main/res/navigation/mobile_navigation.xml | 9 + app/src/main/res/values/colors.xml | 2 + app/src/main/res/values/strings.xml | 15 +- app/src/main/res/values/styles.xml | 15 + 51 files changed, 1855 insertions(+), 218 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt create mode 100644 app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml create mode 100644 app/src/main/res/drawable/ic_baseline_sort_24.xml create mode 100644 app/src/main/res/drawable/ic_outline_account_circle_24.xml create mode 100644 app/src/main/res/drawable/indicator_background.xml create mode 100644 app/src/main/res/drawable/rating_bg_color.xml create mode 100644 app/src/main/res/layout/fragment_library.xml create mode 100644 app/src/main/res/layout/library_viewpager_page.xml create mode 100644 app/src/main/res/layout/loading_poster_dynamic.xml create mode 100644 app/src/main/res/menu/library_menu.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1cbcec68..808c0cc3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -220,6 +220,9 @@ dependencies { // Library/extensions searching with Levenshtein distance implementation("me.xdrop:fuzzywuzzy:1.4.0") + + // color pallette for images -> colors + implementation("androidx.palette:palette-ktx:1.0.0") } tasks.register("androidSourcesJar", Jar::class) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 8c818027..73859021 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -13,7 +13,10 @@ import com.fasterxml.jackson.module.kotlin.KotlinModule import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi +import com.lagradost.cloudstream3.syncproviders.SyncIdName import com.lagradost.cloudstream3.ui.player.SubtitleData +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings +import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.ui.result.UiText import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.Coroutines.threadSafeListOf @@ -510,6 +513,20 @@ abstract class MainAPI { open val hasMainPage = false open val hasQuickSearch = false + /** + * A set of which ids the provider can open with getLoadUrl() + * If the set contains SyncIdName.Imdb then getLoadUrl() can be started with + * an Imdb class which inherits from SyncId. + * + * getLoadUrl() is then used to get page url based on that ID. + * + * Example: + * "tt6723592" -> getLoadUrl(ImdbSyncId("tt6723592")) -> "mainUrl/imdb/tt6723592" -> load("mainUrl/imdb/tt6723592") + * + * This is used to launch pages from personal lists or recommendations using IDs. + **/ + open val supportedSyncNames = setOf() + open val supportedTypes = setOf( TvType.Movie, TvType.TvSeries, @@ -580,6 +597,14 @@ abstract class MainAPI { open fun getVideoInterceptor(extractorLink: ExtractorLink): Interceptor? { return null } + + /** + * Get the load() url based on a sync ID like IMDb or MAL. + * Only contains SyncIds based on supportedSyncUrls. + **/ + open suspend fun getLoadUrl(name: SyncIdName, id: String): String? { + return null + } } /** Might need a different implementation for desktop*/ diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 857eaa6a..5720b7a7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -388,6 +388,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { val isNavVisible = listOf( R.id.navigation_home, R.id.navigation_search, + R.id.navigation_library, R.id.navigation_downloads, R.id.navigation_settings, R.id.navigation_download_child, @@ -438,6 +439,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { nav_view?.isVisible = isNavVisible && !landscape nav_rail_view?.isVisible = isNavVisible && landscape + + // Hide library on TV since it is not supported yet :( + val isTrueTv = isTrueTvSettings() + nav_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv + nav_rail_view?.menu?.findItem(R.id.navigation_library)?.isVisible = !isTrueTv } //private var mCastSession: CastSession? = null diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt deleted file mode 100644 index 208db14b..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/AnilistRedirector.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.lagradost.cloudstream3.metaproviders - -import com.lagradost.cloudstream3.ErrorLoadingException -import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis -import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniListApi -import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi -import com.lagradost.cloudstream3.utils.SyncUtil - -object SyncRedirector { - val syncApis = SyncApis - - suspend fun redirect(url: String, preferredUrl: String): String { - for (api in syncApis) { - if (url.contains(api.mainUrl)) { - val otherApi = when (api.name) { - aniListApi.name -> "anilist" - malApi.name -> "myanimelist" - else -> return url - } - - return SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> - realUrl.contains(preferredUrl) - } ?: run { - throw ErrorLoadingException("Page does not exist on $preferredUrl") - } - } - } - return url - } -} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt new file mode 100644 index 00000000..75e96bec --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/SyncRedirector.kt @@ -0,0 +1,56 @@ +package com.lagradost.cloudstream3.metaproviders + +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.syncproviders.SyncIdName + +object SyncRedirector { + val syncApis = SyncApis + private val syncIds = + listOf( + SyncIdName.MyAnimeList to Regex("""myanimelist\.net\/anime\/(\d+)"""), + SyncIdName.Anilist to Regex("""anilist\.co\/anime\/(\d+)""") + ) + + suspend fun redirect( + url: String, + providerApi: MainAPI + ): String { + // Deprecated since providers should do this instead! + + // Tries built in ID -> ProviderUrl + /* + for (api in syncApis) { + if (url.contains(api.mainUrl)) { + val otherApi = when (api.name) { + aniListApi.name -> "anilist" + malApi.name -> "myanimelist" + else -> return url + } + + SyncUtil.getUrlsFromId(api.getIdFromUrl(url), otherApi).firstOrNull { realUrl -> + realUrl.contains(providerApi.mainUrl) + }?.let { + return it + } +// ?: run { +// throw ErrorLoadingException("Page does not exist on $preferredUrl") +// } + } + } + */ + + // Tries provider solution + // This goes through all sync ids and finds supported id by said provider + return syncIds.firstNotNullOfOrNull { (syncName, syncRegex) -> + if (providerApi.supportedSyncNames.contains(syncName)) { + syncRegex.find(url)?.value?.let { + suspendSafeApiCall { + providerApi.getLoadUrl(syncName, it) + } + } + } else null + } ?: url + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt index f09bf8fe..f17086c1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -13,6 +13,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val openSubtitlesApi = OpenSubtitlesApi(0) val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() + val localListApi = LocalList() // used to login via app intent val OAuth2Apis @@ -29,7 +30,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { // used for active syncing val SyncApis get() = listOf( - SyncRepo(malApi), SyncRepo(aniListApi) + SyncRepo(malApi), SyncRepo(aniListApi), SyncRepo(localListApi) ) val inAppAuths diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt index 5aa56a02..8c76c5bf 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncAPI.kt @@ -1,10 +1,31 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.UiText +import me.xdrop.fuzzywuzzy.FuzzySearch + +enum class SyncIdName { + Anilist, + MyAnimeList, + Trakt, + Imdb, + LocalList +} interface SyncAPI : OAuth2API { + /** + * Set this to true if the user updates something on the list like watch status or score + **/ + var requireLibraryRefresh: Boolean val mainUrl: String + /** + * Allows certain providers to open pages from + * library links. + **/ + val syncIdName: SyncIdName + /** -1 -> None 0 -> Watching @@ -22,7 +43,9 @@ interface SyncAPI : OAuth2API { suspend fun search(name: String): List? - fun getIdFromUrl(url : String) : String + suspend fun getPersonalLibrary(): LibraryMetadata? + + fun getIdFromUrl(url: String): String data class SyncSearchResult( override val name: String, @@ -42,7 +65,7 @@ interface SyncAPI : OAuth2API { val score: Int?, val watchedEpisodes: Int?, var isFavorite: Boolean? = null, - var maxEpisodes : Int? = null, + var maxEpisodes: Int? = null, ) data class SyncResult( @@ -63,9 +86,9 @@ interface SyncAPI : OAuth2API { var genres: List? = null, var synonyms: List? = null, var trailers: List? = null, - var isAdult : Boolean? = null, + var isAdult: Boolean? = null, var posterUrl: String? = null, - var backgroundPosterUrl : String? = null, + var backgroundPosterUrl: String? = null, /** In unixtime */ var startDate: Long? = null, @@ -76,4 +99,61 @@ interface SyncAPI : OAuth2API { var prevSeason: SyncSearchResult? = null, var actors: List? = null, ) + + + data class Page( + val title: UiText, var items: List + ) { + fun sort(method: ListSorting?, query: String? = null) { + items = when (method) { + ListSorting.Query -> + if (query != null) { + items.sortedBy { + -FuzzySearch.partialRatio( + query.lowercase(), it.name.lowercase() + ) + } + } else items + ListSorting.RatingHigh -> items.sortedBy { -(it.personalRating ?: 0) } + ListSorting.RatingLow -> items.sortedBy { (it.personalRating ?: 0) } + ListSorting.AlphabeticalA -> items.sortedBy { it.name } + ListSorting.AlphabeticalZ -> items.sortedBy { it.name }.reversed() + ListSorting.UpdatedNew -> items.sortedBy { it.lastUpdatedUnixTime?.times(-1) } + ListSorting.UpdatedOld -> items.sortedBy { it.lastUpdatedUnixTime } + else -> items + } + } + } + + data class LibraryMetadata( + val allLibraryLists: List, + val supportedListSorting: Set + ) + + data class LibraryList( + val name: UiText, + val items: List + ) + + data class LibraryItem( + override val name: String, + override val url: String, + /** + * Unique unchanging string used for data storage. + * This should be the actual id when you change scores and status + * since score changes from library might get added in the future. + **/ + val syncId: String, + val episodesCompleted: Int?, + val episodesTotal: Int?, + /** Out of 100 */ + val personalRating: Int?, + val lastUpdatedUnixTime: Long?, + override val apiName: String, + override var type: TvType?, + override var posterUrl: String?, + override var posterHeaders: Map?, + override var quality: SearchQuality?, + override var id: Int? = null, + ) : SearchResponse } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt index b621e81a..85b877e0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/SyncRepo.kt @@ -11,26 +11,38 @@ class SyncRepo(private val repo: SyncAPI) { val icon = repo.icon val mainUrl = repo.mainUrl val requiresLogin = repo.requiresLogin + val syncIdName = repo.syncIdName + var requireLibraryRefresh: Boolean + get() = repo.requireLibraryRefresh + set(value) { + repo.requireLibraryRefresh = value + } suspend fun score(id: String, status: SyncAPI.SyncStatus): Resource { return safeApiCall { repo.score(id, status) } } - suspend fun getStatus(id : String) : Resource { + suspend fun getStatus(id: String): Resource { return safeApiCall { repo.getStatus(id) ?: throw ErrorLoadingException("No data") } } - suspend fun getResult(id : String) : Resource { + suspend fun getResult(id: String): Resource { return safeApiCall { repo.getResult(id) ?: throw ErrorLoadingException("No data") } } - suspend fun search(query : String) : Resource> { + suspend fun search(query: String): Resource> { return safeApiCall { repo.search(query) ?: throw ErrorLoadingException() } } - fun hasAccount() : Boolean { + suspend fun getPersonalLibrary(): Resource { + return safeApiCall { repo.getPersonalLibrary() ?: throw ErrorLoadingException() } + } + + fun hasAccount(): Boolean { return normalSafeApiCall { repo.loginInfo() != null } ?: false } - fun getIdFromUrl(url : String) : String = repo.getIdFromUrl(url) + fun getIdFromUrl(url: String): String? = normalSafeApiCall { + repo.getIdFromUrl(url) + } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt index d4742d94..7d9de43a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/AniListApi.kt @@ -1,10 +1,10 @@ package com.lagradost.cloudstream3.syncproviders.providers +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* 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.setKey import com.lagradost.cloudstream3.mvvm.logError @@ -12,6 +12,9 @@ import com.lagradost.cloudstream3.mvvm.suspendSafeApiCall import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.toJson @@ -27,10 +30,12 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { override val key = "6871" override val redirectUrl = "anilistlogin" override val idPrefix = "anilist" + override var requireLibraryRefresh = true override var mainUrl = "https://anilist.co" override val icon = R.drawable.ic_anilist_icon override val requiresLogin = false override val createAccountUrl = "$mainUrl/signup" + override val syncIdName = SyncIdName.Anilist override fun loginInfo(): AuthAPI.LoginInfo? { // context.getUser(true)?. @@ -45,6 +50,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } override fun logOut() { + requireLibraryRefresh = true removeAccountKeys() } @@ -64,8 +70,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { switchToNewAccount() setKey(accountId, ANILIST_UNIXTIME_KEY, endTime) setKey(accountId, ANILIST_TOKEN_KEY, token) - setKey(ANILIST_SHOULD_UPDATE_LIST, true) val user = getUser() + requireLibraryRefresh = true return user != null } @@ -140,7 +146,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { this.name, recMedia.id?.toString() ?: return@mapNotNull null, getUrlFromId(recMedia.id), - recMedia.coverImage?.large ?: recMedia.coverImage?.medium + recMedia.coverImage?.extraLarge ?: recMedia.coverImage?.large + ?: recMedia.coverImage?.medium ) }, trailers = when (season.trailer?.site?.lowercase()?.trim()) { @@ -170,7 +177,9 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) + ).also { + requireLibraryRefresh = requireLibraryRefresh || it + } } companion object { @@ -181,7 +190,6 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { const val ANILIST_TOKEN_KEY: String = "anilist_token" // anilist token for api const val ANILIST_USER_KEY: String = "anilist_user" // user data like profile const val ANILIST_CACHED_LIST: String = "anilist_cached_list" - const val ANILIST_SHOULD_UPDATE_LIST: String = "anilist_should_update_list" private fun fixName(name: String): String { return name.lowercase(Locale.ROOT).replace(" ", "") @@ -219,7 +227,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { romaji } idMal - coverImage { medium large } + coverImage { medium large extraLarge } averageScore } } @@ -232,7 +240,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { format id idMal - coverImage { medium large } + coverImage { medium large extraLarge } averageScore title { english @@ -292,15 +300,13 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { val shows = searchShows(name.replace(blackListRegex, "")) shows?.data?.Page?.media?.find { - malId ?: "NONE" == it.idMal.toString() + (malId ?: "NONE") == it.idMal.toString() }?.let { return it } val filtered = shows?.data?.Page?.media?.filter { - ( - it.startDate.year ?: year.toString() == year.toString() - || year == null - ) + (((it.startDate.year ?: year.toString()) == year.toString() + || year == null)) } filtered?.forEach { it.title.romaji?.let { romaji -> @@ -312,14 +318,14 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } // Changing names of these will show up in UI - enum class AniListStatusType(var value: Int) { - Watching(0), - Completed(1), - Paused(2), - Dropped(3), - Planning(4), - ReWatching(5), - None(-1) + enum class AniListStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + Paused(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + Planning(4, R.string.type_plan_to_watch), + ReWatching(5, R.string.type_re_watching), + None(-1, R.string.none) } fun fromIntToAnimeStatus(inp: Int): AniListStatusType {//= AniListStatusType.values().first { it.value == inp } @@ -335,7 +341,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { } } - fun convertAnilistStringToStatus(string: String): AniListStatusType { + fun convertAniListStringToStatus(string: String): AniListStatusType { return fromIntToAnimeStatus(aniListStatusString.indexOf(string)) } @@ -526,7 +532,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { app.post( "https://graphql.anilist.co/", headers = mapOf( - "Authorization" to "Bearer " + (getAuth() ?: return@suspendSafeApiCall null), + "Authorization" to "Bearer " + (getAuth() + ?: return@suspendSafeApiCall null), if (cache) "Cache-Control" to "max-stale=$maxStale" else "Cache-Control" to "no-cache" ), cacheTime = 0, @@ -575,7 +582,8 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { data class CoverImage( @JsonProperty("medium") val medium: String?, - @JsonProperty("large") val large: String? + @JsonProperty("large") val large: String?, + @JsonProperty("extraLarge") val extraLarge: String? ) data class Media( @@ -602,7 +610,29 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("score") val score: Int, @JsonProperty("private") val private: Boolean, @JsonProperty("media") val media: Media - ) + ) { + fun toLibraryItem(): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + // English title first + this.media.title.english ?: this.media.title.romaji + ?: this.media.synonyms.firstOrNull() + ?: "", + "https://anilist.co/anime/${this.media.id}/", + this.media.id.toString(), + this.progress, + this.media.episodes, + this.score, + this.updatedAt.toLong(), + "AniList", + TvType.Anime, + this.media.coverImage.extraLarge ?: this.media.coverImage.large + ?: this.media.coverImage.medium, + null, + null, + null + ) + } + } data class Lists( @JsonProperty("status") val status: String?, @@ -617,40 +647,59 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { @JsonProperty("MediaListCollection") val MediaListCollection: MediaListCollection ) - fun getAnilistListCached(): Array? { + private fun getAniListListCached(): Array? { return getKey(ANILIST_CACHED_LIST) as? Array } - suspend fun getAnilistAnimeListSmart(): Array? { + private suspend fun getAniListAnimeListSmart(): Array? { if (getAuth() == null) return null if (checkToken()) return null - return if (getKey(ANILIST_SHOULD_UPDATE_LIST, true) == true) { - val list = getFullAnilistList()?.data?.MediaListCollection?.lists?.toTypedArray() + return if (requireLibraryRefresh) { + val list = getFullAniListList()?.data?.MediaListCollection?.lists?.toTypedArray() if (list != null) { setKey(ANILIST_CACHED_LIST, list) - setKey(ANILIST_SHOULD_UPDATE_LIST, false) } list } else { - getAnilistListCached() + getAniListListCached() } } - private suspend fun getFullAnilistList(): FullAnilistList? { - var userID: Int? = null - /** WARNING ASSUMES ONE USER! **/ - getKeys(ANILIST_USER_KEY)?.forEach { key -> - getKey(key, null)?.let { - userID = it.id - } - } + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getAniListAnimeListSmart()?.groupBy { + convertAniListStringToStatus(it.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.entries.map { entry -> entry.toLibraryItem() } }.flatten() + } ?: emptyMap() - val fixedUserID = userID ?: return null + // To fill empty lists when AniList does not return them + val baseMap = + AniListStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) + ) + } + + private suspend fun getFullAniListList(): FullAnilistList? { + /** WARNING ASSUMES ONE USER! **/ + + val userID = getKey(accountId, ANILIST_USER_KEY)?.id ?: return null val mediaType = "ANIME" val query = """ - query (${'$'}userID: Int = $fixedUserID, ${'$'}MEDIA: MediaType = $mediaType) { + query (${'$'}userID: Int = $userID, ${'$'}MEDIA: MediaType = $mediaType) { MediaListCollection (userId: ${'$'}userID, type: ${'$'}MEDIA) { lists { status @@ -661,7 +710,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { startedAt { year month day } updatedAt progress - score + score (format: POINT_100) private media { @@ -677,7 +726,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { english romaji } - coverImage { medium } + coverImage { extraLarge large medium } synonyms nextAiringEpisode { timeUntilAiring diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt new file mode 100644 index 00000000..0b081220 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/LocalList.kt @@ -0,0 +1,100 @@ +package com.lagradost.cloudstream3.syncproviders.providers + +import androidx.fragment.app.FragmentActivity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.AuthAPI +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.WatchType +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.utils.Coroutines.ioWork +import com.lagradost.cloudstream3.utils.DataStoreHelper.getAllWatchStateIds +import com.lagradost.cloudstream3.utils.DataStoreHelper.getBookmarkedData +import com.lagradost.cloudstream3.utils.DataStoreHelper.getResultWatchState + +class LocalList : SyncAPI { + override val name = "Local" + override val icon: Int = R.drawable.ic_baseline_storage_24 + override val requiresLogin = false + override val createAccountUrl: Nothing? = null + override val idPrefix = "local" + override var requireLibraryRefresh = true + + override fun loginInfo(): AuthAPI.LoginInfo { + return AuthAPI.LoginInfo( + null, + null, + 0 + ) + } + + override fun logOut() { + + } + + override val key: String = "" + override val redirectUrl = "" + override suspend fun handleRedirect(url: String): Boolean { + return true + } + + override fun authenticate(activity: FragmentActivity?) { + } + + override val mainUrl = "" + override val syncIdName = SyncIdName.LocalList + override suspend fun score(id: String, status: SyncAPI.SyncStatus): Boolean { + return true + } + + override suspend fun getStatus(id: String): SyncAPI.SyncStatus? { + return null + } + + override suspend fun getResult(id: String): SyncAPI.SyncResult? { + return null + } + + override suspend fun search(name: String): List? { + return null + } + + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata? { + val watchStatusIds = ioWork { + getAllWatchStateIds()?.map { id -> + Pair(id, getResultWatchState(id)) + } + }?.distinctBy { it.first } ?: return null + + val list = ioWork { + watchStatusIds.groupBy { + it.second.stringRes + }.mapValues { group -> + group.value.mapNotNull { + getBookmarkedData(it.first)?.toLibraryItem(it.first.toString()) + } + } + } + + val baseMap = WatchType.values().filter { it != WatchType.NONE }.associate { + // None is not something to display + it.stringRes to emptyList() + } + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, +// ListSorting.UpdatedNew, +// ListSorting.UpdatedOld, +// ListSorting.RatingHigh, +// ListSorting.RatingLow, + ) + ) + } + + override fun getIdFromUrl(url: String): String { + return url + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt index c08958ce..5164b606 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/MALApi.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.syncproviders.providers import android.util.Base64 +import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.AcraApplication.Companion.getKey @@ -8,11 +9,15 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ShowStatus +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.syncproviders.AccountManager import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.library.ListSorting +import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject @@ -31,13 +36,15 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { override val redirectUrl = "mallogin" override val idPrefix = "mal" override var mainUrl = "https://myanimelist.net" - val apiUrl = "https://api.myanimelist.net" + private val apiUrl = "https://api.myanimelist.net" override val icon = R.drawable.mal_logo override val requiresLogin = false - + override val syncIdName = SyncIdName.MyAnimeList + override var requireLibraryRefresh = true override val createAccountUrl = "$mainUrl/register.php" override fun logOut() { + requireLibraryRefresh = true removeAccountKeys() } @@ -90,7 +97,9 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { fromIntToAnimeStatus(status.status), status.score, status.watchedEpisodes - ) + ).also { + requireLibraryRefresh = requireLibraryRefresh || it + } } data class MalAnime( @@ -248,10 +257,45 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { const val MAL_USER_KEY: String = "mal_user" // user data like profile const val MAL_CACHED_LIST: String = "mal_cached_list" - const val MAL_SHOULD_UPDATE_LIST: String = "mal_should_update_list" const val MAL_UNIXTIME_KEY: String = "mal_unixtime" // When token expires const val MAL_REFRESH_TOKEN_KEY: String = "mal_refresh_token" // refresh token const val MAL_TOKEN_KEY: String = "mal_token" // anilist token for api + + fun convertToStatus(string: String): MalStatusType { + return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) + } + + enum class MalStatusType(var value: Int, @StringRes val stringRes: Int) { + Watching(0, R.string.type_watching), + Completed(1, R.string.type_completed), + OnHold(2, R.string.type_on_hold), + Dropped(3, R.string.type_dropped), + PlanToWatch(4, R.string.type_plan_to_watch), + None(-1, R.string.type_none) + } + + private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } + return when (inp) { + -1 -> MalStatusType.None + 0 -> MalStatusType.Watching + 1 -> MalStatusType.Completed + 2 -> MalStatusType.OnHold + 3 -> MalStatusType.Dropped + 4 -> MalStatusType.PlanToWatch + 5 -> MalStatusType.Watching + else -> MalStatusType.None + } + } + + private fun parseDateLong(string: String?): Long? { + return try { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse( + string ?: return null + )?.time?.div(1000) + } catch (e: Exception) { + null + } + } } override suspend fun handleRedirect(url: String): Boolean { @@ -275,7 +319,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { switchToNewAccount() storeToken(res) val user = getMalUser() - setKey(MAL_SHOULD_UPDATE_LIST, true) + requireLibraryRefresh = true return user != null } } @@ -308,9 +352,10 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { setKey(accountId, MAL_UNIXTIME_KEY, (token.expires_in + unixTime)) setKey(accountId, MAL_REFRESH_TOKEN_KEY, token.refresh_token) setKey(accountId, MAL_TOKEN_KEY, token.access_token) + requireLibraryRefresh = true } } catch (e: Exception) { - e.printStackTrace() + logError(e) } } @@ -329,7 +374,7 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { ).text storeToken(res) } catch (e: Exception) { - e.printStackTrace() + logError(e) } } @@ -382,7 +427,24 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { data class Data( @JsonProperty("node") val node: Node, @JsonProperty("list_status") val list_status: ListStatus?, - ) + ) { + fun toLibraryItem(): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + this.node.title, + "https://myanimelist.net/anime/${this.node.id}/", + this.node.id.toString(), + this.list_status?.num_episodes_watched, + this.node.num_episodes, + this.list_status?.score?.times(10), + parseDateLong(this.list_status?.updated_at), + "MAL", + TvType.Anime, + this.node.main_picture?.large ?: this.node.main_picture?.medium, + null, + null, + ) + } + } data class Paging( @JsonProperty("next") val next: String? @@ -413,18 +475,43 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return getKey(MAL_CACHED_LIST) as? Array } - suspend fun getMalAnimeListSmart(): Array? { + private suspend fun getMalAnimeListSmart(): Array? { if (getAuth() == null) return null - return if (getKey(MAL_SHOULD_UPDATE_LIST, true) == true) { + return if (requireLibraryRefresh) { val list = getMalAnimeList() setKey(MAL_CACHED_LIST, list) - setKey(MAL_SHOULD_UPDATE_LIST, false) list } else { getMalAnimeListCached() } } + override suspend fun getPersonalLibrary(): SyncAPI.LibraryMetadata { + val list = getMalAnimeListSmart()?.groupBy { + convertToStatus(it.list_status?.status ?: "").stringRes + }?.mapValues { group -> + group.value.map { it.toLibraryItem() } + } ?: emptyMap() + + // To fill empty lists when MAL does not return them + val baseMap = + MalStatusType.values().filter { it.value >= 0 }.associate { + it.stringRes to emptyList() + } + + return SyncAPI.LibraryMetadata( + (baseMap + list).map { SyncAPI.LibraryList(txt(it.key), it.value) }, + setOf( + ListSorting.AlphabeticalA, + ListSorting.AlphabeticalZ, + ListSorting.UpdatedNew, + ListSorting.UpdatedOld, + ListSorting.RatingHigh, + ListSorting.RatingLow, + ) + ) + } + private suspend fun getMalAnimeList(): Array { checkMalToken() var offset = 0 @@ -440,10 +527,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return fullList.toTypedArray() } - fun convertToStatus(string: String): MalStatusType { - return fromIntToAnimeStatus(malStatusAsString.indexOf(string)) - } - private suspend fun getMalAnimeListSlice(offset: Int = 0): MalList? { val user = "@me" val auth = getAuth() ?: return null @@ -557,28 +640,6 @@ class MALApi(index: Int) : AccountManager(index), SyncAPI { return user } - enum class MalStatusType(var value: Int) { - Watching(0), - Completed(1), - OnHold(2), - Dropped(3), - PlanToWatch(4), - None(-1) - } - - private fun fromIntToAnimeStatus(inp: Int): MalStatusType {//= AniListStatusType.values().first { it.value == inp } - return when (inp) { - -1 -> MalStatusType.None - 0 -> MalStatusType.Watching - 1 -> MalStatusType.Completed - 2 -> MalStatusType.OnHold - 3 -> MalStatusType.Dropped - 4 -> MalStatusType.PlanToWatch - 5 -> MalStatusType.Watching - else -> MalStatusType.None - } - } - private suspend fun setScoreRequest( id: Int, status: MalStatusType? = null, diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt index 138084fc..b4c07792 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/AutofitRecyclerView.kt @@ -7,7 +7,8 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import kotlin.math.abs -class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManager(context, _spanCount) { +class GrdLayoutManager(val context: Context, _spanCount: Int) : + GridLayoutManager(context, _spanCount) { override fun onFocusSearchFailed( focused: View, focusDirection: Int, @@ -34,7 +35,7 @@ class GrdLayoutManager(val context: Context, _spanCount: Int) : GridLayoutManage val pos = maxOf(0, getPosition(focused!!) - 2) parent.scrollToPosition(pos) super.onRequestChildFocus(parent, state, child, focused) - } catch (e: Exception){ + } catch (e: Exception) { false } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt index 8a8f90b4..5cf6fc8e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/home/HomeFragment.kt @@ -569,7 +569,7 @@ class HomeFragment : Fragment() { val mutableListOfResponse = mutableListOf() listHomepageItems.clear() - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList( + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList( d.values.toMutableList(), home_master_recycler ) @@ -621,7 +621,7 @@ class HomeFragment : Fragment() { //home_loaded?.isVisible = false } is Resource.Loading -> { - (home_master_recycler?.adapter as? ParentItemAdapter?)?.updateList(listOf()) + (home_master_recycler?.adapter as? ParentItemAdapter)?.updateList(listOf()) home_loading_shimmer?.startShimmer() home_loading?.isVisible = true home_loading_error?.isVisible = false diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt new file mode 100644 index 00000000..1c6af447 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -0,0 +1,393 @@ +package com.lagradost.cloudstream3.ui.library + +import android.app.Activity +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.AlphaAnimation +import androidx.annotation.StringRes +import androidx.appcompat.widget.SearchView +import androidx.core.view.isVisible +import androidx.fragment.app.activityViewModels +import com.google.android.material.tabs.TabLayoutMediator +import com.lagradost.cloudstream3.APIHolder +import com.lagradost.cloudstream3.APIHolder.allProviders +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.mvvm.debugAssert +import com.lagradost.cloudstream3.mvvm.observe +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.syncproviders.SyncIdName +import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment +import com.lagradost.cloudstream3.ui.result.txt +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_LOAD +import com.lagradost.cloudstream3.ui.search.SEARCH_ACTION_SHOW_METADATA +import com.lagradost.cloudstream3.utils.AppUtils.loadResult +import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult +import com.lagradost.cloudstream3.utils.AppUtils.reduceDragSensitivity +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount +import kotlinx.android.synthetic.main.fragment_library.* +import kotlin.math.abs + +const val LIBRARY_FOLDER = "library_folder" + + +enum class LibraryOpenerType(@StringRes val stringRes: Int) { + Default(R.string.default_subtitles), // TODO FIX AFTER MERGE + Provider(R.string.none), + Browser(R.string.browser), + Search(R.string.search), + None(R.string.none), +} + +/** Used to store how the user wants to open said poster */ +data class LibraryOpener( + val openType: LibraryOpenerType, + val providerData: ProviderLibraryData?, +) + +data class ProviderLibraryData( + val apiName: String +) + +class LibraryFragment : Fragment() { + companion object { + fun newInstance() = LibraryFragment() + + /** + * Store which page was last seen when exiting the fragment and returning + **/ + const val VIEWPAGER_ITEM_KEY = "viewpager_item" + } + + private val libraryViewModel: LibraryViewModel by activityViewModels() + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_library, container, false) + } + + override fun onSaveInstanceState(outState: Bundle) { + viewpager?.currentItem?.let { currentItem -> + outState.putInt(VIEWPAGER_ITEM_KEY, currentItem) + } + super.onSaveInstanceState(outState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + context?.fixPaddingStatusbar(search_status_bar_padding) + + sort_fab?.setOnClickListener { + val methods = libraryViewModel.sortingMethods.map { + txt(it.stringRes).asString(view.context) + } + + activity?.showBottomDialog(methods, + libraryViewModel.sortingMethods.indexOf(libraryViewModel.currentSortingMethod), + txt(R.string.sort_by).asString(view.context), + false, + {}, + { + val method = libraryViewModel.sortingMethods[it] + libraryViewModel.sort(method) + }) + } + + main_search?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + libraryViewModel.sort(ListSorting.Query, query) + return true + } + + // This is required to prevent the first text change + // When this is attached it'll immediately send a onQueryTextChange("") + // Which we do not want + var hasInitialized = false + override fun onQueryTextChange(newText: String?): Boolean { + if (!hasInitialized) { + hasInitialized = true + return true + } + + libraryViewModel.sort(ListSorting.Query, newText) + return true + } + }) + + libraryViewModel.reloadPages(false) + + list_selector?.setOnClickListener { + val items = libraryViewModel.availableApiNames + val currentItem = libraryViewModel.currentApiName.value + + activity?.showBottomDialog(items, + items.indexOf(currentItem), + txt(R.string.select_library).asString(it.context), + false, + {}) { index -> + val selectedItem = items.getOrNull(index) ?: return@showBottomDialog + libraryViewModel.switchList(selectedItem) + } + } + + + /** + * Shows a plugin selection dialogue and saves the response + **/ + fun Activity.showPluginSelectionDialog( + key: String, + syncId: SyncIdName, + apiName: String? = null, + ) { + val availableProviders = allProviders.filter { + it.supportedSyncNames.contains(syncId) + }.map { it.name } + + // Add the api if it exists + (APIHolder.getApiFromNameNull(apiName)?.let { listOf(it.name) } ?: emptyList()) + + val baseOptions = listOf( + LibraryOpenerType.Default, + LibraryOpenerType.None, + LibraryOpenerType.Browser, + LibraryOpenerType.Search + ) + + val items = baseOptions.map { txt(it.stringRes).asString(this) } + availableProviders + + val savedSelection = getKey(LIBRARY_FOLDER, key) + val selectedIndex = + when { + savedSelection == null -> 0 + // If provider + savedSelection.openType == LibraryOpenerType.Provider + && savedSelection.providerData?.apiName != null -> { + availableProviders.indexOf(savedSelection.providerData.apiName) + .takeIf { it != -1 } + ?.plus(baseOptions.size) ?: 0 + } + // Else base option + else -> baseOptions.indexOf(savedSelection.openType) + } + + this.showBottomDialog( + items, + selectedIndex, + txt(R.string.open_with).asString(this), + false, + {}, + ) { + val savedData = if (it < baseOptions.size) { + LibraryOpener( + baseOptions[it], + null + ) + } else { + LibraryOpener( + LibraryOpenerType.Provider, + ProviderLibraryData(items[it]) + ) + } + + setKey( + LIBRARY_FOLDER, + key, + savedData, + ) + } + } + + provider_selector?.setOnClickListener { + val syncName = libraryViewModel.currentSyncApi?.syncIdName ?: return@setOnClickListener + activity?.showPluginSelectionDialog(syncName.name, syncName) + } + + viewpager?.setPageTransformer(LibraryScrollTransformer()) + viewpager?.adapter = + viewpager.adapter ?: ViewpagerAdapter(mutableListOf(), { isScrollingDown: Boolean -> + if (isScrollingDown) { + sort_fab?.shrink() + } else { + sort_fab?.extend() + } + }) callback@{ searchClickCallback -> + // To prevent future accidents + debugAssert({ + searchClickCallback.card !is SyncAPI.LibraryItem + }, { + "searchClickCallback ${searchClickCallback.card} is not a LibraryItem" + }) + + val syncId = (searchClickCallback.card as SyncAPI.LibraryItem).syncId + val syncName = + libraryViewModel.currentSyncApi?.syncIdName ?: return@callback + + when (searchClickCallback.action) { + SEARCH_ACTION_SHOW_METADATA -> { + activity?.showPluginSelectionDialog( + syncId, + syncName, + searchClickCallback.card.apiName + ) + } + + SEARCH_ACTION_LOAD -> { + // This basically first selects the individual opener and if that is default then + // selects the whole list opener + val savedListSelection = + getKey(LIBRARY_FOLDER, syncName.name) + val savedSelection = getKey(LIBRARY_FOLDER, syncId).takeIf { + it?.openType != LibraryOpenerType.Default + } ?: savedListSelection + + when (savedSelection?.openType) { + null, LibraryOpenerType.Default -> { + // Prevents opening MAL/AniList as a provider + if (APIHolder.getApiFromNameNull(searchClickCallback.card.apiName) != null) { + activity?.loadSearchResult( + searchClickCallback.card + ) + } else { + // Search when no provider can open + QuickSearchFragment.pushSearch( + activity, + searchClickCallback.card.name + ) + } + } + LibraryOpenerType.None -> {} + LibraryOpenerType.Provider -> + savedSelection.providerData?.apiName?.let { apiName -> + activity?.loadResult( + searchClickCallback.card.url, + apiName, + ) + } + LibraryOpenerType.Browser -> + openBrowser(searchClickCallback.card.url) + LibraryOpenerType.Search -> { + QuickSearchFragment.pushSearch( + activity, + searchClickCallback.card.name + ) + } + } + } + } + } + + viewpager?.offscreenPageLimit = 2 + viewpager?.reduceDragSensitivity() + + val startLoading = Runnable { + gridview?.numColumns = context?.getSpanCount() ?: 3 + gridview?.adapter = + context?.let { LoadingPosterAdapter(it, 6 * 3) } + library_loading_overlay?.isVisible = true + library_loading_shimmer?.startShimmer() + empty_list_textview?.isVisible = false + } + + val stopLoading = Runnable { + gridview?.adapter = null + library_loading_overlay?.isVisible = false + library_loading_shimmer?.stopShimmer() + } + + val handler = Handler(Looper.getMainLooper()) + + observe(libraryViewModel.pages) { resource -> + when (resource) { + is Resource.Success -> { + handler.removeCallbacks(startLoading) + val pages = resource.value + val showNotice = pages.all { it.items.isEmpty() } + empty_list_textview?.isVisible = showNotice + if (showNotice) { + if (libraryViewModel.availableApiNames.size > 1) { + empty_list_textview?.setText(R.string.empty_library_logged_in_message) + } else { + empty_list_textview?.setText(R.string.empty_library_no_accounts_message) + } + } + + (viewpager.adapter as? ViewpagerAdapter)?.pages = pages + // Using notifyItemRangeChanged keeps the animations when sorting + viewpager.adapter?.notifyItemRangeChanged(0, viewpager.adapter?.itemCount ?: 0) + + // Only stop loading after 300ms to hide the fade effect the viewpager produces when updating + // Without this there would be a flashing effect: + // loading -> show old viewpager -> black screen -> show new viewpager + handler.postDelayed(stopLoading, 300) + + savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> + viewpager?.setCurrentItem(currentPos, false) + savedInstanceState.remove(VIEWPAGER_ITEM_KEY) + } + + // Since the animation to scroll multiple items is so much its better to just hide + // the viewpager a bit while the fastest animation is running + fun hideViewpager(distance: Int) { + if (distance < 3) return + + val hideAnimation = AlphaAnimation(1f, 0f).apply { + duration = distance * 50L + fillAfter = true + } + val showAnimation = AlphaAnimation(0f, 1f).apply { + duration = distance * 50L + startOffset = distance * 100L + fillAfter = true + } + viewpager?.startAnimation(hideAnimation) + viewpager?.startAnimation(showAnimation) + } + + TabLayoutMediator( + library_tab_layout, + viewpager, + ) { tab, position -> + tab.text = pages.getOrNull(position)?.title?.asStringNull(context) + tab.view.setOnClickListener { + val currentItem = viewpager?.currentItem ?: return@setOnClickListener + val distance = abs(position - currentItem) + hideViewpager(distance) + } + }.attach() + } + is Resource.Loading -> { + // Only start loading after 200ms to prevent loading cached lists + handler.postDelayed(startLoading, 200) + } + is Resource.Failure -> { + stopLoading.run() + // No user indication it failed :( + // TODO + } + } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + (viewpager.adapter as? ViewpagerAdapter)?.rebind() + super.onConfigurationChanged(newConfig) + } +} + +class MenuSearchView(context: Context) : SearchView(context) { + override fun onActionViewCollapsed() { + super.onActionViewCollapsed() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt new file mode 100644 index 00000000..8aafbdd6 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryScrollTransformer.kt @@ -0,0 +1,17 @@ +package com.lagradost.cloudstream3.ui.library + +import android.view.View +import androidx.viewpager2.widget.ViewPager2 +import kotlinx.android.synthetic.main.library_viewpager_page.view.* +import kotlin.math.roundToInt + +class LibraryScrollTransformer : ViewPager2.PageTransformer { + override fun transformPage(page: View, position: Float) { + val padding = (-position * page.width).roundToInt() + page.page_recyclerview.setPadding( + padding, 0, + -padding, 0 + ) + } +} + diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt new file mode 100644 index 00000000..5f64880c --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryViewModel.kt @@ -0,0 +1,104 @@ +package com.lagradost.cloudstream3.ui.library + +import androidx.annotation.StringRes +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.Resource +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.SyncApis +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import kotlinx.coroutines.delay + +enum class ListSorting(@StringRes val stringRes: Int) { + Query(R.string.none), + RatingHigh(R.string.sort_rating_desc), + RatingLow(R.string.sort_rating_asc), + UpdatedNew(R.string.sort_updated_new), + UpdatedOld(R.string.sort_updated_old), + AlphabeticalA(R.string.sort_alphabetical_a), + AlphabeticalZ(R.string.sort_alphabetical_z), +} + +const val LAST_SYNC_API_KEY = "last_sync_api" + +class LibraryViewModel : ViewModel() { + private val _pages: MutableLiveData>> = MutableLiveData(null) + val pages: LiveData>> = _pages + + private val _currentApiName: MutableLiveData = MutableLiveData("") + val currentApiName: LiveData = _currentApiName + + private val availableSyncApis + get() = SyncApis.filter { it.hasAccount() } + + var currentSyncApi = availableSyncApis.let { allApis -> + val lastSelection = getKey(LAST_SYNC_API_KEY) + availableSyncApis.firstOrNull { it.name == lastSelection } ?: allApis.firstOrNull() + } + private set(value) { + field = value + setKey(LAST_SYNC_API_KEY, field?.name) + } + + val availableApiNames: List + get() = availableSyncApis.map { it.name } + + var sortingMethods = emptyList() + private set + + var currentSortingMethod: ListSorting? = sortingMethods.firstOrNull() + private set + + fun switchList(name: String) { + currentSyncApi = availableSyncApis[availableApiNames.indexOf(name)] + _currentApiName.postValue(currentSyncApi?.name) + reloadPages(true) + } + + fun sort(method: ListSorting, query: String? = null) { + val currentList = pages.value ?: return + currentSortingMethod = method + (currentList as? Resource.Success)?.value?.forEachIndexed { _, page -> + page.sort(method, query) + } + _pages.postValue(currentList) + } + + fun reloadPages(forceReload: Boolean) { + // Only skip loading if its not forced and pages is not empty + if (!forceReload && (pages.value as? Resource.Success)?.value?.isNotEmpty() == true && + currentSyncApi?.requireLibraryRefresh != true + ) return + + ioSafe { + currentSyncApi?.let { repo -> + _currentApiName.postValue(repo.name) + _pages.postValue(Resource.Loading()) + val libraryResource = repo.getPersonalLibrary() + if (libraryResource is Resource.Failure) { + _pages.postValue(libraryResource) + return@let + } + val library = (libraryResource as? Resource.Success)?.value ?: return@let + + sortingMethods = library.supportedListSorting.toList() + currentSortingMethod = null + + repo.requireLibraryRefresh = false + + val pages = library.allLibraryLists.map { + SyncAPI.Page( + it.name, + it.items + ) + } + + _pages.postValue(Resource.Success(pages)) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt new file mode 100644 index 00000000..a637133b --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LoadingPosterAdapter.kt @@ -0,0 +1,37 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.FrameLayout +import android.widget.LinearLayout +import android.widget.ListPopupWindow.MATCH_PARENT +import android.widget.RelativeLayout +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.android.synthetic.main.loading_poster_dynamic.view.* +import kotlin.math.roundToInt +import kotlin.math.sqrt + +class LoadingPosterAdapter(context: Context, private val itemCount: Int) : + BaseAdapter() { + private val inflater: LayoutInflater = LayoutInflater.from(context) + + override fun getCount(): Int { + return itemCount + } + + override fun getItem(position: Int): Any? { + return null + } + + override fun getItemId(position: Int): Long { + return position.toLong() + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return convertView ?: inflater.inflate(R.layout.loading_poster_dynamic, parent, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt new file mode 100644 index 00000000..2435f8be --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/PageAdapter.kt @@ -0,0 +1,130 @@ +package com.lagradost.cloudstream3.ui.library + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.AutofitRecyclerView +import com.lagradost.cloudstream3.ui.search.SearchClickCallback +import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.utils.AppUtils +import com.lagradost.cloudstream3.utils.UIHelper.toPx +import kotlinx.android.synthetic.main.search_result_grid_expanded.view.* +import kotlin.math.roundToInt + + +class PageAdapter( + override val items: MutableList, + private val resView: AutofitRecyclerView, + val clickCallback: (SearchClickCallback) -> Unit +) : + AppUtils.DiffAdapter(items) { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return LibraryItemViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.search_result_grid_expanded, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is LibraryItemViewHolder -> { + holder.bind(items[position], position) + } + } + } + + private fun isDark(color: Int): Boolean { + return ColorUtils.calculateLuminance(color) < 0.5 + } + + fun getDifferentColor(color: Int, ratio: Float = 0.7f): Int { + return if (isDark(color)) { + ColorUtils.blendARGB(color, Color.WHITE, ratio) + } else { + ColorUtils.blendARGB(color, Color.BLACK, ratio) + } + } + + inner class LibraryItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val cardView: ImageView = itemView.imageView + + private val compactView = false//itemView.context.getGridIsCompact() + private val coverHeight: Int = + if (compactView) 80.toPx else (resView.itemWidth / 0.68).roundToInt() + + fun bind(item: SyncAPI.LibraryItem, position: Int) { + /** https://stackoverflow.com/questions/8817522/how-to-get-color-code-of-image-view */ + + SearchResultBuilder.bind( + this@PageAdapter.clickCallback, + item, + position, + itemView, + colorCallback = { palette -> + AcraApplication.context?.let { ctx -> + val defColor = ContextCompat.getColor(ctx, R.color.ratingColorBg) + var bg = palette.getDarkVibrantColor(defColor) + if (bg == defColor) { + bg = palette.getDarkMutedColor(defColor) + } + if (bg == defColor) { + bg = palette.getVibrantColor(defColor) + } + + val fg = + getDifferentColor(bg)//palette.getVibrantColor(ContextCompat.getColor(ctx,R.color.ratingColor)) + itemView.text_rating.apply { + setTextColor(ColorStateList.valueOf(fg)) + } + itemView.text_rating_holder?.backgroundTintList = ColorStateList.valueOf(bg) + itemView.watchProgress?.apply { + progressTintList = ColorStateList.valueOf(fg) + progressBackgroundTintList = ColorStateList.valueOf(bg) + } + } + } + ) + + // See searchAdaptor for this, it basically fixes the height + if (!compactView) { + cardView.apply { + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + coverHeight + ) + } + } + + val showProgress = item.episodesCompleted != null && item.episodesTotal != null + itemView.watchProgress.isVisible = showProgress + if (showProgress) { + itemView.watchProgress.max = item.episodesTotal!! + itemView.watchProgress.progress = item.episodesCompleted!! + } + + itemView.imageText.text = item.name + + val showRating = (item.personalRating ?: 0) != 0 + itemView.text_rating_holder.isVisible = showRating + if (showRating) { + // We want to show 8.5 but not 8.0 hence the replace + val rating = ((item.personalRating ?: 0).toDouble() / 10).toString() + .replace(".0", "") + + itemView.text_rating.text = "★ $rating" + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt new file mode 100644 index 00000000..33a40386 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/ViewpagerAdapter.kt @@ -0,0 +1,90 @@ +package com.lagradost.cloudstream3.ui.library + +import android.os.Build +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.doOnAttach +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnFlingListener +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.syncproviders.SyncAPI +import com.lagradost.cloudstream3.ui.search.SearchClickCallback +import com.lagradost.cloudstream3.utils.UIHelper.getSpanCount +import kotlinx.android.synthetic.main.library_viewpager_page.view.* + +class ViewpagerAdapter( + var pages: List, + val scrollCallback: (isScrollingDown: Boolean) -> Unit, + val clickCallback: (SearchClickCallback) -> Unit +) : RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return PageViewHolder( + LayoutInflater.from(parent.context) + .inflate(R.layout.library_viewpager_page, parent, false) + ) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is PageViewHolder -> { + holder.bind(pages[position], unbound.remove(position)) + } + } + } + + private val unbound = mutableSetOf() + /** + * Used to mark all pages for re-binding and forces all items to be refreshed + * Without this the pages will still use the same adapters + **/ + fun rebind() { + unbound.addAll(0..pages.size) + this.notifyItemRangeChanged(0, pages.size) + } + + inner class PageViewHolder(private val itemViewTest: View) : + RecyclerView.ViewHolder(itemViewTest) { + fun bind(page: SyncAPI.Page, rebind: Boolean) { + itemView.page_recyclerview?.spanCount = + this@PageViewHolder.itemView.context.getSpanCount() ?: 3 + + if (itemViewTest.page_recyclerview?.adapter == null || rebind) { + // Only add the items after it has been attached since the items rely on ItemWidth + // Which is only determined after the recyclerview is attached. + // If this fails then item height becomes 0 when there is only one item + itemViewTest.page_recyclerview?.doOnAttach { + itemViewTest.page_recyclerview?.adapter = PageAdapter( + page.items.toMutableList(), + itemViewTest.page_recyclerview, + clickCallback + ) + } + } else { + (itemViewTest.page_recyclerview?.adapter as? PageAdapter)?.updateList(page.items) + itemViewTest.page_recyclerview?.scrollToPosition(0) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + itemViewTest.page_recyclerview.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> + val diff = scrollY - oldScrollY + if (diff == 0) return@setOnScrollChangeListener + + scrollCallback.invoke(diff > 0) + } + } else { + itemViewTest.page_recyclerview.onFlingListener = object : OnFlingListener() { + override fun onFling(velocityX: Int, velocityY: Int): Boolean { + scrollCallback.invoke(velocityY > 0) + return false + } + } + } + + } + } + + override fun getItemCount(): Int { + return pages.size + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt index ad3d9eb8..ba57d2de 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/quicksearch/QuickSearchFragment.kt @@ -220,7 +220,7 @@ class QuickSearchFragment : Fragment() { when (it) { is Resource.Success -> { it.value.let { data -> - (quick_search_autofit_results?.adapter as? SearchAdapter?)?.updateList( + (quick_search_autofit_results?.adapter as? SearchAdapter)?.updateList( context?.filterSearchResultByFilmQuality(data) ?: data ) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 9cfbf45c..2e2e46b7 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -277,7 +277,7 @@ open class ResultFragment : ResultTrailerPlayer() { private var downloadButton: EasyDownloadButton? = null override fun onDestroyView() { updateUIListener = null - (result_episodes?.adapter as EpisodeAdapter?)?.killAdapter() + (result_episodes?.adapter as? EpisodeAdapter)?.killAdapter() downloadButton?.dispose() super.onDestroyView() @@ -458,7 +458,7 @@ open class ResultFragment : ResultTrailerPlayer() { temporary_no_focus?.requestFocus() } - (result_episodes?.adapter as? EpisodeAdapter?)?.updateList(episodes.value) + (result_episodes?.adapter as? EpisodeAdapter)?.updateList(episodes.value) if (isTv && hasEpisodes) main { delay(500) @@ -687,7 +687,7 @@ open class ResultFragment : ResultTrailerPlayer() { val newList = list.filter { it.isSynced && it.hasAccount } result_mini_sync?.isVisible = newList.isNotEmpty() - (result_mini_sync?.adapter as? ImageAdapter?)?.updateList(newList.mapNotNull { it.icon }) + (result_mini_sync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon }) } var currentSyncProgress = 0 @@ -900,7 +900,7 @@ open class ResultFragment : ResultTrailerPlayer() { result_cast_items?.isVisible = d.actors != null - (result_cast_items?.adapter as ActorAdaptor?)?.apply { + (result_cast_items?.adapter as? ActorAdaptor)?.apply { updateList(d.actors ?: emptyList()) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt index 9bae8753..b38e1765 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentPhone.kt @@ -485,7 +485,7 @@ class ResultFragmentPhone : ResultFragment() { result_recommendations?.post { rec?.let { list -> - (result_recommendations?.adapter as SearchAdapter?)?.updateList(list.filter { it.apiName == matchAgainst }) + (result_recommendations?.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst }) } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt index d5cab1a6..2bd8ff0f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragmentTv.kt @@ -107,7 +107,7 @@ class ResultFragmentTv : ResultFragment() { result_recommendations?.isGone = isInvalid result_recommendations_holder?.isGone = isInvalid val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName - (result_recommendations?.adapter as SearchAdapter?)?.updateList(rec?.filter { it.apiName == matchAgainst } + (result_recommendations?.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst } ?: emptyList()) rec?.map { it.apiName }?.distinct()?.let { apiNames -> diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt index 6ed32b15..6817af6a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt @@ -13,6 +13,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.getId import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.AcraApplication.Companion.setKey @@ -1443,12 +1444,18 @@ class ResultViewModel2 : ViewModel() { val realRecommendations = ArrayList() // TODO: fix - //val apiNames = listOf(GogoanimeProvider().name, NineAnimeProvider().name) - // meta.recommendations?.forEach { rec -> - // apiNames.forEach { name -> - // realRecommendations.add(rec.copy(apiName = name)) - // } - // } + val apiNames = apis.filter { + it.name.contains("gogoanime", true) || + it.name.contains("9anime", true) + }.map { + it.name + } + + meta.recommendations?.forEach { rec -> + apiNames.forEach { name -> + realRecommendations.add(rec.copy(apiName = name)) + } + } recommendations = recommendations?.union(realRecommendations)?.toList() ?: realRecommendations @@ -2143,7 +2150,7 @@ class ResultViewModel2 : ViewModel() { val validUrlResource = safeApiCall { SyncRedirector.redirect( url, - api.mainUrl + api ) } // TODO: fix diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt index ddf559fc..649641c8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchAdaptor.kt @@ -10,12 +10,16 @@ import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.SearchResponse import com.lagradost.cloudstream3.ui.AutofitRecyclerView +import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.UIHelper.IsBottomLayout import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.search_result_compact.view.* import kotlin.math.roundToInt +/** Click */ const val SEARCH_ACTION_LOAD = 0 + +/** Long press */ const val SEARCH_ACTION_SHOW_METADATA = 1 const val SEARCH_ACTION_PLAY_FILE = 2 const val SEARCH_ACTION_FOCUSED = 4 diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt index 4144a042..b4a38216 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchFragment.kt @@ -420,7 +420,7 @@ class SearchFragment : Fragment() { is Resource.Success -> { it.value.let { data -> if (data.isNotEmpty()) { - (search_autofit_results?.adapter as SearchAdapter?)?.updateList(data) + (search_autofit_results?.adapter as? SearchAdapter)?.updateList(data) } } searchExitIcon.alpha = 1f diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt index 3afbb8c0..3447ee32 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/search/SearchResultBuilder.kt @@ -1,12 +1,14 @@ package com.lagradost.cloudstream3.ui.search import android.content.Context +import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView import androidx.cardview.widget.CardView import androidx.core.view.isVisible +import androidx.palette.graphics.Palette import androidx.preference.PreferenceManager import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings @@ -41,6 +43,7 @@ object SearchResultBuilder { nextFocusBehavior: Boolean? = null, nextFocusUp: Int? = null, nextFocusDown: Int? = null, + colorCallback : ((Palette) -> Unit)? = null ) { val cardView: ImageView = itemView.imageView val cardText: TextView? = itemView.imageText @@ -100,7 +103,7 @@ object SearchResultBuilder { cardText?.isVisible = showTitle cardView.isVisible = true - if (!cardView.setImage(card.posterUrl, card.posterHeaders)) { + if (!cardView.setImage(card.posterUrl, card.posterHeaders, colorCallback = colorCallback)) { cardView.setImageResource(R.drawable.default_cover) } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt index bd44a058..d328d226 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsFragment.kt @@ -143,7 +143,7 @@ class PluginsFragment : Fragment() { } observe(pluginViewModel.filteredPlugins) { (scrollToTop, list) -> - (plugin_recycler_view?.adapter as? PluginAdapter?)?.updateList(list) + (plugin_recycler_view?.adapter as? PluginAdapter)?.updateList(list) if (scrollToTop) plugin_recycler_view?.scrollToPosition(0) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt index d563bffa..00dee9b2 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/AppUtils.kt @@ -28,10 +28,12 @@ import androidx.core.text.toSpanned import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.tvprovider.media.tv.* import androidx.tvprovider.media.tv.WatchNextProgram.fromCursor +import androidx.viewpager2.widget.ViewPager2 import com.fasterxml.jackson.module.kotlin.readValue import com.google.android.gms.cast.framework.CastContext import com.google.android.gms.cast.framework.CastState @@ -65,6 +67,7 @@ import okhttp3.Cache import java.io.* import java.net.URL import java.net.URLDecoder +import kotlin.system.measureTimeMillis object AppUtils { fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) { @@ -164,6 +167,18 @@ object AppUtils { return builder.build() } + // https://stackoverflow.com/a/67441735/13746422 + fun ViewPager2.reduceDragSensitivity(f: Int = 4) { + val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView") + recyclerViewField.isAccessible = true + val recyclerView = recyclerViewField.get(this) as RecyclerView + + val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop") + touchSlopField.isAccessible = true + val touchSlop = touchSlopField.get(recyclerView) as Int + touchSlopField.set(recyclerView, touchSlop * f) // "8" was obtained experimentally + } + @SuppressLint("RestrictedApi") fun getAllWatchNextPrograms(context: Context): Set { val COLUMN_WATCH_NEXT_ID_INDEX = 0 @@ -329,6 +344,46 @@ object AppUtils { } } + abstract class DiffAdapter( + open val items: MutableList, + val comparison: (first: T, second: T) -> Boolean = { first, second -> + first.hashCode() == second.hashCode() + } + ) : + RecyclerView.Adapter() { + override fun getItemCount(): Int { + return items.size + } + + fun updateList(newList: List) { + val diffResult = DiffUtil.calculateDiff( + GenericDiffCallback(this.items, newList) + ) + + items.clear() + items.addAll(newList) + + diffResult.dispatchUpdatesTo(this) + } + + inner class GenericDiffCallback( + private val oldList: List, + private val newList: List + ) : + DiffUtil.Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) = + comparison(oldList[oldItemPosition], newList[newItemPosition]) + + override fun getOldListSize() = oldList.size + + override fun getNewListSize() = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) = + oldList[oldItemPosition] == newList[newItemPosition] + } + } + + fun Activity.downloadAllPluginsDialog(repositoryUrl: String, repositoryName: String) { runOnUiThread { val context = this diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt index 80e5d64a..8d51e5ef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -18,13 +18,11 @@ import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.plugins.PLUGINS_KEY import com.lagradost.cloudstream3.plugins.PLUGINS_KEY_LOCAL import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_CACHED_LIST -import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.AniListApi.Companion.ANILIST_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_CACHED_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_REFRESH_TOKEN_KEY -import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_SHOULD_UPDATE_LIST import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_TOKEN_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY @@ -52,12 +50,10 @@ object BackupUtils { // When sharing backup we do not want to transfer what is essentially the password ANILIST_TOKEN_KEY, ANILIST_CACHED_LIST, - ANILIST_SHOULD_UPDATE_LIST, ANILIST_UNIXTIME_KEY, ANILIST_USER_KEY, MAL_TOKEN_KEY, MAL_REFRESH_TOKEN_KEY, - MAL_SHOULD_UPDATE_LIST, MAL_CACHED_LIST, MAL_UNIXTIME_KEY, MAL_USER_KEY, diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt index 9174c481..281c9c44 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream3.utils import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.capitalize import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey @@ -10,6 +11,8 @@ 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.SyncAPI import com.lagradost.cloudstream3.ui.WatchType import com.lagradost.cloudstream3.ui.result.VideoWatchState @@ -51,7 +54,20 @@ object DataStoreHelper { @JsonProperty("year") val year: Int?, @JsonProperty("quality") override var quality: SearchQuality? = null, @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, - ) : SearchResponse + ) : SearchResponse { + fun toLibraryItem(id: String): SyncAPI.LibraryItem { + return SyncAPI.LibraryItem( + name, + url, + id, + null, + null, + null, + null, + apiName, type, posterUrl, posterHeaders, quality, this.id + ) + } + } data class ResumeWatchingResult( @JsonProperty("name") override val name: String, @@ -71,6 +87,9 @@ object DataStoreHelper { @JsonProperty("posterHeaders") override var posterHeaders: Map? = null, ) : SearchResponse + /** + * A datastore wide account for future implementations of a multiple account system + **/ private var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION fun getAllWatchStateIds(): List? { @@ -177,6 +196,7 @@ object DataStoreHelper { fun setBookmarkedData(id: Int?, data: BookmarkedData) { if (id == null) return setKey("$currentAccount/$RESULT_WATCH_STATE_DATA", id.toString(), data) + AccountManager.localListApi.requireLibraryRefresh = true } fun getBookmarkedData(id: Int?): BookmarkedData? { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt index 7dda3e18..e5f2f2dc 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/SyncUtil.kt @@ -4,6 +4,7 @@ package com.lagradost.cloudstream3.utils import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.apis //import com.lagradost.cloudstream3.animeproviders.AniflixProvider import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError @@ -78,17 +79,21 @@ object SyncUtil { return null } - suspend fun getUrlsFromId(id: String, type: String = "anilist") : List { - return arrayListOf() - // val url = - // "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" - // val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() - // val pages = response.pages ?: return emptyList() - // val current = pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values).mapNotNull { it.url }.toMutableList() - // if(type == "anilist") { // TODO MAKE BETTER - // current.add("${AniflixProvider().mainUrl}/anime/$id") - // } - // return current + suspend fun getUrlsFromId(id: String, type: String = "anilist"): List { + val url = + "https://raw.githubusercontent.com/MALSync/MAL-Sync-Backup/master/data/$type/anime/$id.json" + val response = app.get(url, cacheTime = 1, cacheUnit = TimeUnit.DAYS).parsed() + val pages = response.pages ?: return emptyList() + val current = + pages.gogoanime.values.union(pages.nineanime.values).union(pages.twistmoe.values) + .mapNotNull { it.url }.toMutableList() + + if (type == "anilist") { // TODO MAKE BETTER + apis.filter { it.name.contains("Aniflix", ignoreCase = true) }.forEach { + current.add("${it.mainUrl}/anime/$id") + } + } + return current } data class SyncPage( diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index 63b3623d..c300d615 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -9,7 +9,9 @@ import android.content.Context import android.content.pm.PackageManager import android.content.res.Configuration import android.content.res.Resources +import android.graphics.Bitmap import android.graphics.Color +import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle import android.view.* @@ -28,15 +30,21 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.core.graphics.alpha import androidx.core.graphics.blue +import androidx.core.graphics.drawable.toBitmapOrNull import androidx.core.graphics.green import androidx.core.graphics.red import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.navigation.fragment.NavHostFragment +import androidx.palette.graphics.Palette import androidx.preference.PreferenceManager +import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.load.model.GlideUrl import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings @@ -105,7 +113,7 @@ object UIHelper { listView.requestLayout() } - fun Activity?.getSpanCount(): Int? { + fun Context?.getSpanCount(): Int? { val compactView = false val spanCountLandscape = if (compactView) 2 else 6 val spanCountPortrait = if (compactView) 1 else 3 @@ -158,12 +166,27 @@ object UIHelper { return color } + var createPaletteAsyncCache: HashMap = hashMapOf() + fun createPaletteAsync(url: String, bitmap: Bitmap, callback: (Palette) -> Unit) { + createPaletteAsyncCache[url]?.let { palette -> + callback.invoke(palette) + return + } + Palette.from(bitmap).generate { paletteNull -> + paletteNull?.let { palette -> + createPaletteAsyncCache[url] = palette + callback(palette) + } + } + } + fun ImageView?.setImage( url: String?, headers: Map? = null, @DrawableRes errorImageDrawable: Int? = null, - fadeIn: Boolean = true + fadeIn: Boolean = true, + colorCallback: ((Palette) -> Unit)? = null ): Boolean { if (this == null || url.isNullOrBlank()) return false @@ -177,6 +200,33 @@ object UIHelper { else req } + if (colorCallback != null) { + builder.listener(object : RequestListener { + @SuppressLint("CheckResult") + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean + ): Boolean { + resource?.toBitmapOrNull() + ?.let { bitmap -> createPaletteAsync(url, bitmap, colorCallback) } + return false + } + + @SuppressLint("CheckResult") + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean + ): Boolean { + return false + } + }) + } + val res = if (errorImageDrawable != null) builder.error(errorImageDrawable).into(this) else diff --git a/app/src/main/res/color/item_select_color.xml b/app/src/main/res/color/item_select_color.xml index 0d2834dd..3d69c540 100644 --- a/app/src/main/res/color/item_select_color.xml +++ b/app/src/main/res/color/item_select_color.xml @@ -1,5 +1,7 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml new file mode 100644 index 00000000..fc90e300 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_sort_24.xml b/app/src/main/res/drawable/ic_baseline_sort_24.xml new file mode 100644 index 00000000..96d46231 --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_sort_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_baseline_star_24.xml b/app/src/main/res/drawable/ic_baseline_star_24.xml index ab099425..2dcadb7f 100644 --- a/app/src/main/res/drawable/ic_baseline_star_24.xml +++ b/app/src/main/res/drawable/ic_baseline_star_24.xml @@ -1,5 +1,5 @@ - + android:width="12dp" xmlns:android="http://schemas.android.com/apk/res/android"> diff --git a/app/src/main/res/drawable/ic_outline_account_circle_24.xml b/app/src/main/res/drawable/ic_outline_account_circle_24.xml new file mode 100644 index 00000000..cc564471 --- /dev/null +++ b/app/src/main/res/drawable/ic_outline_account_circle_24.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/drawable/indicator_background.xml b/app/src/main/res/drawable/indicator_background.xml new file mode 100644 index 00000000..ef44fb7c --- /dev/null +++ b/app/src/main/res/drawable/indicator_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/rating_bg_color.xml b/app/src/main/res/drawable/rating_bg_color.xml new file mode 100644 index 00000000..60e62bab --- /dev/null +++ b/app/src/main/res/drawable/rating_bg_color.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ad29d22a..b6290865 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -35,9 +35,9 @@ --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/library_viewpager_page.xml b/app/src/main/res/layout/library_viewpager_page.xml new file mode 100644 index 00000000..f69f68b5 --- /dev/null +++ b/app/src/main/res/layout/library_viewpager_page.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/app/src/main/res/layout/loading_poster_dynamic.xml b/app/src/main/res/layout/loading_poster_dynamic.xml new file mode 100644 index 00000000..11855acb --- /dev/null +++ b/app/src/main/res/layout/loading_poster_dynamic.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 25b3f67d..47fd7cd3 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -5,62 +5,109 @@ android:id="@+id/search_result_root" android:layout_width="match_parent" android:layout_height="wrap_content" - android:clickable="true" android:focusable="true" android:foreground="@drawable/outline_drawable" android:orientation="vertical"> - + android:layout_height="wrap_content"> - - android:layout_height="match_parent" - android:contentDescription="@string/search_poster_img_des" - android:duplicateParentState="true" - android:foreground="?android:attr/selectableItemBackgroundBorderless" - android:scaleType="centerCrop" - tools:src="@drawable/example_poster" /> - - - - + + android:id="@+id/text_quality" + style="@style/TypeButton" /> - - - + + + + + + + + + + + + + + + + - - + + - \ No newline at end of file + diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml index 3a5e0929..cb620bb8 100644 --- a/app/src/main/res/menu/bottom_nav_menu.xml +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -1,20 +1,23 @@ - + android:id="@+id/navigation_home" + android:icon="@drawable/home_alt" + android:title="@string/title_home" /> + android:id="@+id/navigation_search" + android:icon="@drawable/search_icon" + android:title="@string/title_search" /> + android:id="@+id/navigation_library" + android:icon="@drawable/ic_outline_account_circle_24" + android:title="@string/library" /> + android:id="@+id/navigation_downloads" + android:icon="@drawable/netflix_download" + android:title="@string/title_downloads" /> + \ No newline at end of file diff --git a/app/src/main/res/menu/library_menu.xml b/app/src/main/res/menu/library_menu.xml new file mode 100644 index 00000000..f21d998d --- /dev/null +++ b/app/src/main/res/menu/library_menu.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 14d750a0..d71eeb06 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -144,6 +144,15 @@ app:popEnterAnim="@anim/enter_anim" app:popExitAnim="@anim/exit_anim" /> + + #F53B66 #BEC8FF ?attr/colorPrimaryDark + #4C3115 + #FFA662 #FF6F63 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6c10d865..cee6bccc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,6 +110,7 @@ Genres Share Open In Browser + Browser Skip Loading Loading… Watching @@ -229,6 +230,7 @@ Storage permissions missing. Please try again. Error backing up %s Search + Library Accounts Updates and backup Info @@ -616,5 +618,16 @@ Legacy PackageInstaller App will be updated upon exit - + Sort by + Sort + Rating (High to Low) + Rating (Low to High) + Updated (New to Old) + Updated (Old to New) + Alphabetical (A to Z) + Alphabetical (Z to A) + Select Library + Open with + Looks like your library is empty :(\nLogin to a library account or add shows to your local library + Looks like this list is empty, try switching to another one diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b14cd189..2540bf34 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -90,11 +90,13 @@ @font/google_sans 0dp + + + + + + From 2771dcb6124726ade7aec8a9e46655f5ff6987f9 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Sat, 28 Jan 2023 22:38:55 +0000 Subject: [PATCH 19/47] update version code --- app/build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 808c0cc3..93e386b6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -47,8 +47,8 @@ android { minSdk = 21 targetSdk = 33 - versionCode = 56 - versionName = "3.5.0" + versionCode = 57 + versionName = "4.0.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") @@ -253,4 +253,4 @@ tasks.withType().configureEach { } } } -} \ No newline at end of file +} From 9905618a47d9f3d47abe5a4f627fce0733e1f072 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:15:28 +0100 Subject: [PATCH 20/47] Added safe mode file as a last resort --- .../lagradost/cloudstream3/MainActivity.kt | 9 +++++++-- .../cloudstream3/plugins/PluginManager.kt | 19 +++++++++++++++++-- app/src/main/res/values/strings.xml | 1 + 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 5720b7a7..eddec15e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -347,7 +347,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { } } - var lastPopup : SearchResponse? = null + var lastPopup: SearchResponse? = null fun loadPopup(result: SearchResponse) { lastPopup = result viewModel.load( @@ -716,7 +716,12 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) - if (lastError == null) { + + if (PluginManager.checkSafeModeFile()) { + normalSafeApiCall { + showToast(this, R.string.safe_mode_file, Toast.LENGTH_LONG) + } + } else if (lastError == null) { ioSafe { getKey(USER_SELECTED_HOMEPAGE_API)?.let { homeApi -> mainPluginsLoadedEvent.invoke(loadSinglePlugin(this@MainActivity, homeApi)) diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt index 54fe5d75..28dfc092 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -144,8 +144,10 @@ object PluginManager { return getKey(PLUGINS_KEY_LOCAL) ?: emptyArray() } - private val LOCAL_PLUGINS_PATH = - Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/plugins" + private val CLOUD_STREAM_FOLDER = + Environment.getExternalStorageDirectory().absolutePath + "/Cloudstream3/" + + private val LOCAL_PLUGINS_PATH = CLOUD_STREAM_FOLDER + "plugins" public var currentlyLoading: String? = null @@ -421,6 +423,19 @@ object PluginManager { afterPluginsLoadedEvent.invoke(forceReload) } + /** + * This can be used to override any extension loading to fix crashes! + * @return true if safe mode file is present + **/ + fun checkSafeModeFile(): Boolean { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + return files?.any() ?: false + } + /** * @return True if successful, false if not * */ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cee6bccc..a4375ce3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -630,4 +630,5 @@ Open with Looks like your library is empty :(\nLogin to a library account or add shows to your local library Looks like this list is empty, try switching to another one + Safe mode file found!\nNot loading any extensions on startup until file is removed. From fd2648df459e4cdc0089ea5119de6da349d211c5 Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 16:31:16 +0100 Subject: [PATCH 21/47] made the checkSafeModeFile() crash-proof --- .../cloudstream3/plugins/PluginManager.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt index 28dfc092..3533d6a8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -428,12 +428,14 @@ object PluginManager { * @return true if safe mode file is present **/ fun checkSafeModeFile(): Boolean { - val folder = File(CLOUD_STREAM_FOLDER) - if (!folder.exists()) return false - val files = folder.listFiles { _, name -> - name.equals("safe", ignoreCase = true) - } - return files?.any() ?: false + return normalSafeApiCall { + val folder = File(CLOUD_STREAM_FOLDER) + if (!folder.exists()) return@normalSafeApiCall false + val files = folder.listFiles { _, name -> + name.equals("safe", ignoreCase = true) + } + files?.any() + } ?: false } /** From 6e9b1cb855fa298f0df3c8b015a4cd8583821ddc Mon Sep 17 00:00:00 2001 From: Blatzar <46196380+Blatzar@users.noreply.github.com> Date: Sun, 29 Jan 2023 23:51:25 +0100 Subject: [PATCH 22/47] Made source dialog fullscreen and added some Extractors --- .../cloudstream3/extractors/Filesim.kt | 41 ++++++++++++++----- .../cloudstream3/extractors/GuardareStream.kt | 5 +++ .../cloudstream3/ui/player/GeneratorPlayer.kt | 10 ++--- .../cloudstream3/utils/ExtractorApi.kt | 2 + .../layout/player_select_source_and_subs.xml | 2 + 5 files changed, 42 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt index 8e3dc730..bc910a7e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Filesim.kt @@ -1,32 +1,51 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import java.net.URI + +class FileMoon : Filesim() { + override val mainUrl = "https://filemoon.to" + override val name = "FileMoon" +} open class Filesim : ExtractorApi() { override val name = "Filesim" override val mainUrl = "https://files.im" override val requiresReferer = false - override suspend fun getUrl(url: String, referer: String?): List { - val sources = mutableListOf() + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { with(app.get(url).document) { - this.select("script").map { script -> + this.select("script").forEach { script -> if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val data = getAndUnpack(script.data()).substringAfter("sources:[").substringBefore("]") - tryParseJson>("[$data]")?.map { - M3u8Helper.generateM3u8( - name, - it.file, - "$mainUrl/", - ).forEach { m3uData -> sources.add(m3uData) } + val data = getAndUnpack(script.data()) + val foundData = Regex("""sources:\[(.*?)]""").find(data)?.groupValues?.get(1) ?: return@forEach + val fixedData = foundData.replace("file:", """"file":""") + + parseJson>("[$fixedData]").forEach { + callback.invoke( + ExtractorLink( + name, + name, + it.file, + "$mainUrl/", + Qualities.Unknown.value, + URI(it.file).path.endsWith(".m3u8") + ) + ) } } } } - return sources } private data class ResponseSource( diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt index f25cb5ba..2adc00d5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/GuardareStream.kt @@ -6,6 +6,11 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* +class Vanfem : GuardareStream() { + override var name = "Vanfem" + override var mainUrl = "https://vanfem.com/" +} + class CineGrabber : GuardareStream() { override var name = "CineGrabber" override var mainUrl = "https://cinegrabber.com" diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index e15dcee6..67f58195 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -11,9 +11,7 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.view.inputmethod.EditorInfo import android.widget.* -import android.widget.TextView.OnEditorActionListener import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.core.animation.addListener @@ -528,7 +526,7 @@ class GeneratorPlayer : FullScreenPlayer() { } } - var selectSourceDialog: AlertDialog? = null + var selectSourceDialog: Dialog? = null // var selectTracksDialog: AlertDialog? = null override fun showMirrorsDialogue() { @@ -540,10 +538,8 @@ class GeneratorPlayer : FullScreenPlayer() { player.handleEvent(CSPlayerEvent.Pause) val currentSubtitles = sortSubs(currentSubs) - val sourceBuilder = AlertDialog.Builder(ctx, R.style.AlertDialogCustomBlack) - .setView(R.layout.player_select_source_and_subs) - - val sourceDialog = sourceBuilder.create() + val sourceDialog = Dialog(ctx, R.style.AlertDialogCustomBlack) + sourceDialog.setContentView(R.layout.player_select_source_and_subs) selectSourceDialog = sourceDialog diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt index bd4f8705..1ad3639b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -291,6 +291,7 @@ val extractorApis: MutableList = arrayListOf( Supervideo(), GuardareStream(), CineGrabber(), + Vanfem(), // StreamSB.kt works // SBPlay(), @@ -321,6 +322,7 @@ val extractorApis: MutableList = arrayListOf( DesuDrive(), Filesim(), + FileMoon(), Linkbox(), Acefile(), SpeedoStream(), diff --git a/app/src/main/res/layout/player_select_source_and_subs.xml b/app/src/main/res/layout/player_select_source_and_subs.xml index 369f6776..067e4ad5 100644 --- a/app/src/main/res/layout/player_select_source_and_subs.xml +++ b/app/src/main/res/layout/player_select_source_and_subs.xml @@ -44,6 +44,7 @@ android:nextFocusLeft="@id/sort_subtitles" android:nextFocusRight="@id/apply_btt" android:requiresFadingEdge="vertical" + tools:layout_height="100dp" tools:listitem="@layout/sort_bottom_single_choice" /> @@ -117,6 +118,7 @@ android:nextFocusLeft="@id/sort_providers" android:nextFocusRight="@id/cancel_btt" android:requiresFadingEdge="vertical" + tools:layout_height="200dp" tools:listfooter="@layout/sort_bottom_footer_add_choice" tools:listitem="@layout/sort_bottom_single_choice" /> From c7c5fa250e177dbf22a9ea463c721c63f3feb79a Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Tue, 31 Jan 2023 09:12:25 +0100 Subject: [PATCH 23/47] Translated using Weblate (Polish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 96.5% (562 of 582 strings) Translated using Weblate (Russian) Currently translated at 91.4% (532 of 582 strings) Translated using Weblate (Greek) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 90.3% (526 of 582 strings) Translated using Weblate (Russian) Currently translated at 88.3% (514 of 582 strings) Translated using Weblate (Russian) Currently translated at 87.9% (512 of 582 strings) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (German) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Italian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (582 of 582 strings) Translated using Weblate (Ukrainian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Polish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Italian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Arabic) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Vietnamese) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Indonesian) Currently translated at 100.0% (581 of 581 strings) Translated using Weblate (Spanish) Currently translated at 100.0% (581 of 581 strings) Co-authored-by: Aitor Salaberria Co-authored-by: Alex Georgiou Co-authored-by: Cliff Heraldo Co-authored-by: Cloudburst <18114966+C10udburst@users.noreply.github.com> Co-authored-by: Hosted Weblate Co-authored-by: JL Pilgram Co-authored-by: Julian Co-authored-by: NickSkier Co-authored-by: Rex_sa Co-authored-by: Sdarfeesh Co-authored-by: Skrripy Co-authored-by: Translator-3000 Co-authored-by: eightyy8 Co-authored-by: gallegonovato Co-authored-by: gnu-ewm Co-authored-by: kaajjo Co-authored-by: tuan041 Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ar/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/de/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/el/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/es/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/id/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/it/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/pl/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/ru/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/uk/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/vi/ Translate-URL: https://hosted.weblate.org/projects/cloudstream/app/zh_Hans/ Translation: Cloudstream/App --- app/src/main/res/values-ar/strings.xml | 17 ++++ app/src/main/res/values-de/strings.xml | 17 ++++ app/src/main/res/values-el/strings.xml | 20 ++++- app/src/main/res/values-es/strings.xml | 17 ++++ app/src/main/res/values-in/strings.xml | 21 ++++- app/src/main/res/values-it/strings.xml | 17 ++++ app/src/main/res/values-pl/strings.xml | 17 ++++ app/src/main/res/values-ru/strings.xml | 107 ++++++++++++++++++++----- app/src/main/res/values-uk/strings.xml | 15 ++++ app/src/main/res/values-vi/strings.xml | 53 ++++++++---- app/src/main/res/values-zh/strings.xml | 17 ++++ 11 files changed, 278 insertions(+), 40 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 3a0c97e7..f318478e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -521,4 +521,21 @@ بدأ التحديث تم تنزيل الإضافة إزالة من المشاهدة + الترتيب الأبجدي (من الألف إلى الياء) + اختر المكتبة + المتصفح + محدث (من الأحدث إلى الأقدم) + يبدو أن هذه القائمة فارغة ، حاول التبديل إلى قائمة أخرى + التقييم (من الأعلى إلى الأدنى) + التقييم (من الأدنى إلى الأعلى) + الترتيب الأبجدي (من ي إلى أ) + يبدو أن مكتبتك فارغة :( +\nتسجيل الدخول إلى حساب مكتبة أو إضافة عروض إلى مكتبتك المحلية + محدث (من القديم إلى الجديد) + فرز حسب + افرز + فتح بواسطة + المكتبة + تم العثور على ملف الوضع الآمن! +\nلا يتم تحميل أي ملحقات عند بدء التشغيل حتى تتم إزالة الملف. \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 00aa5b97..63ed5444 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -489,4 +489,21 @@ Die Anwendung wird beim Beenden aktualisiert Das Plugin wurde heruntergeladen Von geschaut entfernen + Bibliothek + Browser + Sortieren nach + Sortieren + Bewertung (gut bis schlecht) + Bewertung (schlecht bis gut) + Aktualisiert (neu bis alt) + Aktualisiert (alt bis neu) + Alphabetisch (A bis Z) + Alphabetisch (Z bis A) + Bibliothek auswählen + Öffnen mit + Sieht aus, als wäre deine Bibliothek leer :( +\nMelde dich mit einem Bibliothekskonto an oder füge Titel zu deiner lokalen Bibliothek hinzu + Diese Liste scheint leer zu sein. Versuche, zu einer anderen Liste zu wechseln. + Datei für abgesicherten Modus gefunden! +\nBeim Start werden keine Erweiterungen geladen, bis die Datei entfernt wird. \ No newline at end of file diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index dc7088cc..0d0b7fb2 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -314,7 +314,7 @@ Αναφορά κατάρρευσης Τι θα θέλατε να δείτε Έγινε - Πρόσθετα + Extensions Προσθήκη αποθετηρίου Όνομα αποθετηρίου Σύνδεσμος αποθετηρίου @@ -490,4 +490,22 @@ Το πρόσθετο κατέβει Ενημέρωση ξεκίνησε Η εφαρμογή θα ενημερωθεί κατά την έξοδο + Αλφαβητικά (Ω προς Α) + Ταξινόμηση + Κριτική (Χαμηλή προς Υψηλή) + Ενημερωμένο (Καινούριο προς παλιό) + Ενημερωμένο (Παλιό προς Καινούργιο) + Βιβλιοθήκη + Κριτική (Υψηλή προς χαμηλή) + Ταξινόμηση με βάση + Αλφαβητικά (Α προς Ω) + Διάλεξε βιβλιοθήκη + Φαίνεται πως η λίστα είναι άδεια, δοκίμασε να μεταβείς σε μία άλλη + Αφαίρεση από παρακολουθημένα + Περιηγητής + Άνοιγμα με + Φαίνεται πως η βιβλιοθήκη σου είναι άδεια :( +\nΣυνδέσου σε έναν λογαριασμό που έχει βιβλιοθήκη, ή πρόσθεσε σειρές στην τοπική βιβλιοθήκη σου + Βρέθηκε αρχείο Ασφαλούς Λειτουργίας! +\nΔεν πρόκειται να φορτωθούν extensions κατά το ξεκίνημα μέχρι να διαγραφεί το αρχείο. \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 08ae5bf1..37e5c431 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -489,4 +489,21 @@ Actualización iniciada Complemento descargado Quitar de visto + Ordenar por + Ordenar + Valoración (más a menos) + Valoración (menos a más) + Actualizado (nuevo a viejo) + Actualizado (viejo a nuevo) + Alfabéticamente (A a Z) + Navegador + Biblioteca + Parece que esta lista está vacía, intenta cambiar a otra + Alfabéticamente (Z a A) + Seleccionar biblioteca + Abrir con + Parece que tu biblioteca está vacía :( +\nInicia sesión en una cuenta de biblioteca o añade series desde tu biblioteca local + ¡Se encontró un archivo en modo seguro! +\nNo cargar ninguna extensión al inicio hasta que se elimine el archivo. \ No newline at end of file diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 83dc6ee9..96c5950b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -453,7 +453,7 @@ Semua fitur tambahkan dimatikan karena crash, untuk memudahkanmu mencari penyebab crash. Kode bahasa (en) Ambil dari internet - Putar vidio di bahasa ini + Putar video di bahasa ini Tambah Repositori Pilih ini untuk menghapus semua repositori plugin Lewati pengaturan @@ -483,7 +483,7 @@ Gerakan Beberapa perangkat tidak mendukung penginstal paket mode baru. Coba mode lama jika pembaruan tidak dapat diinstal. Aksi - Referensi + Referer Ya Pasang dulu fitur tambahan Semua Bahasa @@ -512,4 +512,21 @@ Aplikasi akan diperbaharui pada saat keluar Pembaharuan Dimulai Hapus dari tontonan + Browser + Pilih pustaka + Yahh daftar pustaka kamu kosong :( +\nMasuk ke akun pustaka atau tambah perlihatkan ke lokal pustaka kamu + Pustaka + Urutkan berdasar + Urutkan + Peringkat (Rendah ke Tinggi) + Update (Lama ke Terbaru) + Peringkat (Tinggi ke Rendah) + Update (Terbaru ke Lama) + Abjad (A ke Z) + Abjad (Z ke A) + Buka dengan + Yahh daftar ini kosong, coba ganti ke yang lain + Mode aman file ditemukan! +\nTidak memuat ekstensi pada startup sampai berkas dihapus. \ No newline at end of file diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b4ba292e..419818a2 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -511,4 +511,21 @@ Aggiornamento avviato Plugin scaricato Rimuovi dai già visti + Browser + Ordina per + Punteggio (Decrescente) + Punteggio (Crescente) + Aggiornato (Da nuovo a vecchio) + Aggiornato (Da vecchio a nuovo) + Alfabetico (A - Z) + Alfabetico (Z - A) + Sembra che la tua libreria sia vuota :( +\nAccedi a un account di libreria o aggiungi degli show alla tua libreria locale + Seleziona libreria + Apri con + Libreria + Ordina + Sembra che questa lista sia vuota, prova a passare a un\'altra + File \"safe mode\" trovato! +\nAll\'avvio non sarà caricata alcuna estensione finchè il file non verrà rimosso. \ No newline at end of file diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 244ae2e1..e4b74300 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -492,4 +492,21 @@ Rozpoczęto aktualizację Pobrano rozszerzenie Usuń z obejrzanych + Przeglądarka + Data aktualizacji (od nowego do starego) + Sortuj według + Sortuj + Otwórz za pomocą + Ocena (od najwyższej do najniższej) + Ocena (od najniższej do najwyższej) + Data aktualizacji (od starego do nowego) + Alfabetycznie (od A do Z) + Alfabetycznie (od Z do A) + Wybierz bibliotekę + Biblioteka + Wygląda na to, że twoja biblioteka jest pusta :( +\nZaloguj się na swoje konto lub dodaj programy do swojej lokalnej biblioteki + Wygląda na to, że ta lista jest pusta, spróbuj przełączyć się na inną + Znaleziono plik trybu bezpiecznego. +\nRozszerzenia nie zostaną wczytane, dopóki plik nie zostanie usunięty. \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 39e74794..537bdb7d 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -25,12 +25,12 @@ Серия %d будет выпущен в Плакат \@нить/результат_плокат_картинка_ - Серия плакат - Главный плакат + Постер Эпизода + Главный постер Следующий случайный Вернуться Изменить поставщика - Фон предпросмотр + Предпросмотр фона Скорость (%.2fx) Оценили: %.1f Новое обновление найдено! @@ -48,31 +48,31 @@ Поиск %s… Нет данных Дополнительные опции - Следующий серия + Следующий эпизод Жанры Поделиться - Открыть в браузер + Открыть в браузере Пропустить загрузку Просмотр Приостановленно Завершено Брошенный - План по смотреть + План посмотреть Никто Пересмотрю Смотреть фильм - Проиграть трейлер + Воспроизвести трейлер Воспроизвести Livestream Источники Субтитры - Проиграть серия + Воспроизвести эпизод Повторная попытка подключение… Вернуться - Скачали + Скачано Скачивание Скачать приостановленный Скачать начатый - Скачать отменено + Скачать отменённый Скачать выполнено Инфо Обновление началось @@ -116,8 +116,8 @@ Удалить файл Проиграть файл Внутренняя память - Скачать резюме - Приостановить скачать + Продолжить Скачать + Приостановить скачивание Отключить автоматическое информирование об ошибках Импортируйте шрифты поместив их в %s Продолжить смотреть @@ -174,7 +174,7 @@ Э Эпизоды не найдены Удалить файл - Возобновить + Продолжить -30 +30 Это будет удалено безвозвратно%s @@ -193,7 +193,7 @@ Другое Ошибка загрузки, проверьте разрешения хранилища Копировать ссылку - Автоматическая загрузка + Автоскачивание Загрузка. Зеркало Сезон Аниме приложение от тех же разработчиков @@ -217,7 +217,7 @@ Торрент Документальный Азиатская драма - Общий + Основные Провайдеры Макет Расширения @@ -240,7 +240,7 @@ Обновление не найдено Изменить размер Источник - Проверьте наличие обновления + Проверить обновления Клон сайта DNS через HTTPS Удалить сайт @@ -248,7 +248,7 @@ Синхронизация субтитров Добавить клон существующего сайта с другим URL-адресом Используется для обхода блокировок интернет провайдера - Путь загрузки + Путь скачивания учитывая бенен Обновить Основной цвет @@ -304,7 +304,7 @@ Пропустить это обновление URL-адрес NGINX-сервера Создать учётную запись - Добавить трекинг + Добавить слежение Добавлено %s Синхронизировать Оценено @@ -393,7 +393,7 @@ Отключено: %d %s %s %s аутентифицировано - Не удалось перейти к %s + Не удается логин на %s Макс Минимум Очертание @@ -416,20 +416,83 @@ Главное Источник Случайный - Скоро будет… + Скоро… Этикетка Sub Фон - Вид + Oтoбpaжeниe Трейлер %s (отключено) Следующий В CloudStream по умолчанию не установлены сайты. Вам необходимо установить сайты из репозиториев. \n -\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем связать сайт репозитория в приложении. +\nИз-за безмозглой DMCA-атаки со стороны Sky UK Limited 🤮 мы не можем привязать сайт репозитория в приложении. \n \nПрисоединяйтесь к нашему Discord или ищите в интернете. Недопустимые данные Разрешение и название Предыдущий Разрешение + Браузер + Библиотека + Обновленный (старый - новый) + Алфавитный (А - Я) + Алфавитный (Я - А) + Выбрать библиотеку + Открыть с + Похоже, ваша библиотека пуста :( +\nВойдите в аккаунт с библиотекой или добавьте сериалы в локальную библиотеку + Сортировка + Открытый список + Рейтинг (высокий - низкий) + Рейтинг (низкий - высокий) + Обновленный (новый - старый) + Сортировать по + PackageInstaller + Кодировка субтитров + Загрузить из файла + Рейтинг: %s + Скачано %d %s + Все %s уже скачаны + Начата загрузка %d %s… + Не скачано: %d + Скачать все плагины из этого репозитория\? + Включен безопасный режим + Скачано: %d + Обновлено %d плагинов + Загрузить из интернета + Загрузка обновления приложения… + Недопустимый URL + Применить при перезапуске + Отчеты ошибках + Что вы хотите увидеть + Смотрите видео на этих языках + Скачано файл + Изображение постера + Пакетная загрузка + Скачайте список сайтов, который вы хотите использовать + Отображать Аниме с Дубляжом/Субтитрами + Включить NSFW на поддерживаемых провайдерах + Убрать скрытые субтитры из субтитров + Дополнительно + Изменить вид интерфейса, чтобы соответствовать устройству + Аудио дорожки + Это также удалит все плагины репозитория + Просмотреть репозитории сообщества + Видео дорожки + Все расширения были отключены из-за сбоя, чтобы помочь вам найти то, которое вызывает проблемы. + Повтор + Слишком много текста. Не удалось сохранить в буфер обмена. + Установка обновления приложения… + Не удалось установить новую версию приложения + Файл безопасного режима найден! +\nНе загружаются никакие расширения при запуске, пока файл не будет удален. + Приложение будет обновлено после выхода + Похоже, этот список пуст, попробуйте переключиться на другой + Все субтитры заглавными + Показывать всплывающие окна для пропуска вступления/заключения + Фильтровать по предпочитаемому языку медиа + Неверный ID + Ссылка на стрим + Отображать рандомную кнопку на Главной странице + Рандомная кнопка \ No newline at end of file diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 93e51c84..821d062a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -489,4 +489,19 @@ Програму не знайдено Змішаний опенінг Видалити з переглянутого + За оновленням (від старого до нового) + За оновленням (від нового до старого) + Бібліотека + Сортувати + За рейтингом (від високого до низького) + Сортувати за + За алфавітом (від А до Я) + За рейтингом (від низького до високого) + Схоже, ваша бібліотека порожня :( +\nУвійдіть в обліковий запис бібліотеки або додайте серіали до вашої локальної бібліотеки + За алфавітом (від Я до А) + Виберіть бібліотеку + Відкрити з + Браузер + Схоже, цей список порожній, спробуйте перейти до іншого \ No newline at end of file diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 3aa5cf69..db647b5d 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -66,7 +66,7 @@ Tải thành công Trực tiếp Đã có lỗi xảy ra - Bộ nhớ máy + Bộ nhớ trong Lồng Tiếng Phụ Đề Xóa Tệp @@ -118,7 +118,7 @@ Không tìm thấy thông tin Hiển thị Logcat 🐈 Chế độ cửa sổ nhỏ - Tiếp tục xem phim khi thoát app hoặc đang tìm kiếm + Tiếp tục xem phim khi thoát ứng dụng hoặc khi tìm kiếm Bật nút thu phóng khi xem Xóa khoảng đen của phim Phụ đề @@ -159,7 +159,7 @@ Không gửi dữ liệu Hiển thị tập phụ cho anime Hiển thị trailer - Hiển thị poster từ kitsu + Hiển thị poster từ Kitsu Ẩn chất lượng video khi tìm kiếm Tự động cập nhật plugin Hiển thị thông báo cập nhật App @@ -210,7 +210,7 @@ Không có phụ đề Mặc Định Còn trống - Đã dùng + Đã sử dụng App Phim Lẻ @@ -228,7 +228,7 @@ Phim Lẻ Phim Bộ Hoạt Hình - \@string/anime + Anime \@string/ova Torrent Phim Tài Liệu @@ -261,7 +261,7 @@ Khóa Thu Phóng Tuỳ chọn - Tập tiếp + Tua nhanh Không hiện lại Bỏ qua Cập nhật @@ -272,8 +272,8 @@ Thời lượng bộ nhớ đệm Dung lượng video cache Xoá hình ảnh và video - Sẽ gây lỗi nếu đặt quá cao. Không thay đổi nếu máy có dung lượng ram thấp, chẳng hạn như Android TV hoặc điện thoại cũ - Sẽ thể gây lỗi trên các máy có dung lượng lưu trữ thấp, chẳng hạn như thiết bị Android TV nếu bạn đặt nó quá cao + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng ram thấp như Android TV. + Sẽ gây lỗi nếu đặt quá cao trên máy có dung lượng lưu trữ thấp như Android TV. DNS over HTTPS Rất hữu ích để bỏ chặn ISP Sao chép trang web @@ -410,7 +410,7 @@ Đã xoá plugin Không tải được %s 18+ - Bắt đầu tải %d %s + Bắt đầu tải %d %s… Tải xuống %d %s thành công Toàn bộ %s đã được tải xuống Tải hàng loạt @@ -422,7 +422,11 @@ Đã tải: %d Đã vô hiệu: %d Không tải: %d - Thêm kho lưu trữ để cài tiện ích + CloudStream không có sẵn trang web nào. Bạn cần cài đặt các trang web từ kho lưu trữ. +\n +\nDo Sky UK Limited đã gỡ xuống theo DMCA một cách thiếu suy nghĩ 🤮 chúng tôi không thể cài sẵn trang web. +\n +\nHãy tham gia Discord của chúng tôi hoặc tìm kiếm trực tuyến. Xem kho lưu trữ của cộng đồng Danh sách công khai In hoa toàn bộ phụ đề @@ -437,18 +441,18 @@ Xem thông tin sự cố Lịch sử Đánh dấu là đã xem - Tự động tải plugin + Tự động tải xuống plugin Thiết lập lại Bộ cài APK Một số máy không hỗ trợ trình cài đặt gói mới. Hãy thử tùy chọn cũ nếu các bản cập nhật không cài đặt. %s %d%s - Xem Trailer - Tự động tải plugins còn thiếu. + Xem giới thiệu + Tự động tải plugin còn thiếu. Bắt đầu cập nhật Liên kết Danh sách HLS Trình phát ưu tiên - Trình phát mặc địng + Trình phát mặc định Đánh giá: %s Không Phiên bản @@ -487,7 +491,7 @@ Danh đề Giới thiệu Xoá lịch sử - Hiển thị cửa sổ tua cho mở đầu/kết thúc + Hiển thị nút tua nhanh cho mở đầu/kết thúc Văn bản quá dài. Không thể lưu vào bộ nhớ tạm. Xoá khỏi đã xem Bạn có chắc muốn thoát\? @@ -496,4 +500,23 @@ Đang cài bản cập nhật… Không thể cài đặt phiên bản mới Ứng dụng sẽ được cập nhật khi thoát + Thư viện + Trình duyệt + Plugin đã tải + Mặc định + Tải lên (Mới đến Cũ) + Tải lên (Cũ đến Mới) + Thư viện của bạn đang trống :( +\nHãy đăng nhập vào thư viện hoặc thêm phim vào thư viện cục bộ + Mở với + Siêu dữ liệu không có sẵn, video sẽ không được tải nếu nó không tồn tại trên trang web. + PackageInstaller + Sắp xếp + Xếp hạng (Cao đến Thấp) + Xếp hạng (Thấp đến Cao) + Chữ cái (Z đến A) + Sắp xếp + Có vẻ như danh sách này trống, hãy thử chuyển sang danh sách khác + Chữ cái (A đến Z) + Chọn Thư viện \ No newline at end of file diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 97a48597..ece917d9 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -537,4 +537,21 @@ 应用退出后将会更新 插件已下载 从已观看中移除 + 发现安全模式文件! +\n启动时不加载任何扩展,直到文件被删除。 + 浏览器 + + 排序方式 + 排序 + 评分(从高到低) + 评分(从低到高) + 更新(从新到旧) + 更新(从旧到新) + 字母排序(从 A 到 Z) + 字母排序(从 Z 到 A) + 选择库 + 打开方式 + 看来您的库是空的 :( +\n登录库账户或添加节目到您的本地库 + 看来此列表是空的,请尝试切换到另一个 \ No newline at end of file From b26a41bdaf8aeab10a7038c991327747bdd74cf5 Mon Sep 17 00:00:00 2001 From: hexated Date: Tue, 31 Jan 2023 14:26:34 +0700 Subject: [PATCH 24/47] fixed VidSrcExtractor --- .../lagradost/cloudstream3/extractors/VidSrcExtractor.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt index b910f9dd..a27bf188 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcExtractor.kt @@ -59,8 +59,8 @@ open class VidSrcExtractor : ExtractorApi() { if (datahash.isNotBlank()) { val links = try { app.get( - "$absoluteUrl/src/$datahash", - referer = "https://source.vidsrc.me/" + "$absoluteUrl/srcrcp/$datahash", + referer = "https://rcp.vidsrc.me/" ).url } catch (e: Exception) { "" @@ -71,7 +71,7 @@ open class VidSrcExtractor : ExtractorApi() { serverslist.amap { server -> val linkfixed = server.replace("https://vidsrc.xyz/", "https://embedsito.com/") - if (linkfixed.contains("/pro")) { + if (linkfixed.contains("/prorcp")) { val srcresponse = app.get(server, referer = absoluteUrl).text val m3u8Regex = Regex("((https:|http:)//.*\\.m3u8)") val srcm3u8 = m3u8Regex.find(srcresponse)?.value ?: return@amap From 490381451b28473fcfc41002856e5daa4c1ec131 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 31 Jan 2023 10:57:11 +0100 Subject: [PATCH 25/47] [skip ci] label issues if provider mentioned --- .github/workflows/issue_action.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/issue_action.yml b/.github/workflows/issue_action.yml index 28b737b3..2a54857c 100644 --- a/.github/workflows/issue_action.yml +++ b/.github/workflows/issue_action.yml @@ -53,6 +53,18 @@ jobs: Please do not report any provider bugs here. This repository does not contain any providers. Please find the appropriate repository and report your issue there or join the [discord](https://discord.gg/5Hus6fM). Found provider name: `${{ steps.provider_check.outputs.name }}` + - name: Label if mentions provider + if: steps.provider_check.outputs.name != 'none' + uses: actions/github-script@v6 + with: + github-token: ${{ steps.generate_token.outputs.token }} + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["possible provider issue"] + }) - name: Add eyes reaction to all issues uses: actions-cool/emoji-helper@v1.0.0 with: From b0921161a3c31c46079c4791f575e538c0b1e153 Mon Sep 17 00:00:00 2001 From: LagradOst <46196380+Blatzar@users.noreply.github.com> Date: Tue, 31 Jan 2023 23:43:29 +0100 Subject: [PATCH 26/47] Nicehttp version bump --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 93e386b6..3c855d28 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -190,7 +190,7 @@ dependencies { // Networking // implementation("com.squareup.okhttp3:okhttp:4.9.2") // implementation("com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.1") - implementation("com.github.Blatzar:NiceHttp:0.4.1") + implementation("com.github.Blatzar:NiceHttp:0.4.2") // To fix SSL fuckery on android 9 implementation("org.conscrypt:conscrypt-android:2.2.1") // Util to skip the URI file fuckery 🙏 From 99887534327c86bcc653dfbc84a24917d34b0fe5 Mon Sep 17 00:00:00 2001 From: no-commit <> Date: Thu, 2 Feb 2023 01:15:24 +0100 Subject: [PATCH 27/47] Library and Light mode improvements. --- .../lagradost/cloudstream3/ui/library/LibraryFragment.kt | 4 +++- .../com/lagradost/cloudstream3/ui/result/ResultFragment.kt | 1 + app/src/main/res/layout/fragment_library.xml | 6 +++--- app/src/main/res/layout/search_result_grid_expanded.xml | 2 +- app/src/main/res/values/styles.xml | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt index 1c6af447..d7c06c4e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/library/LibraryFragment.kt @@ -333,8 +333,10 @@ class LibraryFragment : Fragment() { handler.postDelayed(stopLoading, 300) savedInstanceState?.getInt(VIEWPAGER_ITEM_KEY)?.let { currentPos -> + if (currentPos < 0) return@let viewpager?.setCurrentItem(currentPos, false) - savedInstanceState.remove(VIEWPAGER_ITEM_KEY) + // Using remove() sets the key to 0 instead of removing it + savedInstanceState.putInt(VIEWPAGER_ITEM_KEY, -1) } // Since the animation to scroll multiple items is so much its better to just hide diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 2e2e46b7..68dd1c0e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -974,6 +974,7 @@ open class ResultFragment : ResultTrailerPlayer() { chip.isCheckable = false chip.isFocusable = false chip.isClickable = false + chip.setTextColor(context.colorFromAttribute(R.attr.textColor)) addView(chip) } } diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index f9012148..985d055d 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -166,12 +166,12 @@ app:layout_scrollFlags="noScroll" app:tabGravity="center" app:tabIndicator="@drawable/indicator_background" - app:tabIndicatorColor="@color/textColor" + app:tabIndicatorColor="?attr/white" app:tabIndicatorGravity="center" app:tabIndicatorHeight="30dp" app:tabMode="scrollable" - app:tabSelectedTextColor="@color/lightTextColor" + app:tabSelectedTextColor="?attr/primaryBlackBackground" app:tabTextAppearance="@style/TabNoCaps" - app:tabTextColor="@color/textColor" /> + app:tabTextColor="?attr/textColor" /> diff --git a/app/src/main/res/layout/search_result_grid_expanded.xml b/app/src/main/res/layout/search_result_grid_expanded.xml index 47fd7cd3..cf6ab3b2 100644 --- a/app/src/main/res/layout/search_result_grid_expanded.xml +++ b/app/src/main/res/layout/search_result_grid_expanded.xml @@ -23,7 +23,7 @@ android:elevation="10dp" app:cardBackgroundColor="?attr/primaryGrayBackground" app:cardCornerRadius="@dimen/rounded_image_radius" - app:layout_constraintDimensionRatio="1:1.414" + app:layout_constraintDimensionRatio="1:1.5" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 2540bf34..78c62c69 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -100,6 +100,7 @@