diff --git a/README.md b/README.md index 0035daf7..1c9ca358 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ You can find the list of community-maintained extension repositories [here ](https://recloudstream.github.io/repos/) -[![Discord](https://img.shields.io/discord/737724143126052974?style=for-the-badge)](https://discord.gg/5Hus6fM) +[![Discord](https://invidget.switchblade.xyz/5Hus6fM)](https://discord.gg/5Hus6fM) ***Features:*** + **AdFree**, No ads whatsoever @@ -21,6 +21,7 @@ You can find the list of community-maintained extension repositories [here ***The list of supported languages:*** * 🇱🇧 Arabic +* 🇭🇷 Croatian * 🇨🇿 Czech * 🇳🇱 Dutch * 🇬🇧 English diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt b/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt index 5372d0be..55e09251 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/DohProviders.kt @@ -64,4 +64,24 @@ fun OkHttpClient.Builder.addAdGuardDns() = ( "94.140.14.140", "94.140.14.141", ) - )) \ No newline at end of file + )) + +fun OkHttpClient.Builder.addDNSWatchDns() = ( + addGenericDns( + "https://resolver2.dns.watch/dns-query", + // https://dns.watch/ + listOf( + "84.200.69.80", + "84.200.70.40", + ) + )) + +fun OkHttpClient.Builder.addQuad9Dns() = ( + addGenericDns( + "https://dns.quad9.net/dns-query", + // https://www.quad9.net/service/service-addresses-and-features + listOf( + "9.9.9.9", + "149.112.112.112", + ) + )) \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt index 85e9d318..03ec6ae8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/RequestsHelper.kt @@ -36,6 +36,8 @@ fun Requests.initClient(context: Context): OkHttpClient { 2 -> addCloudFlareDns() // 3 -> addOpenDns() 4 -> addAdGuardDns() + 5 -> addDNSWatchDns() + 6 -> addQuad9Dns() } } // Needs to be build as otherwise the other builders will change this object 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 b1fc69e7..b5441b94 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/PluginManager.kt @@ -339,9 +339,7 @@ object PluginManager { } plugins[filePath] = pluginInstance classLoaders[loader] = pluginInstance - if (data.url != null) { // TODO: make this cleaner - urlPlugins[data.url] = pluginInstance - } + urlPlugins[data.url ?: filePath] = pluginInstance pluginInstance.load(activity) Log.i(TAG, "Loaded plugin ${data.internalName} successfully") currentlyLoading = null @@ -382,6 +380,7 @@ object PluginManager { classLoaders.values.removeIf { v -> v == plugin } plugins.remove(absolutePath) + urlPlugins.values.removeIf { v -> v == plugin } } /** diff --git a/app/src/main/java/com/lagradost/cloudstream3/plugins/VotingApi.kt b/app/src/main/java/com/lagradost/cloudstream3/plugins/VotingApi.kt index 7b9dc8c7..ab702d71 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/plugins/VotingApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/plugins/VotingApi.kt @@ -1,11 +1,15 @@ package com.lagradost.cloudstream3.plugins import android.util.Log +import android.widget.Toast +import com.lagradost.cloudstream3.AcraApplication.Companion.context import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.R import java.security.MessageDigest import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.cloudstream3.utils.Coroutines.main object VotingApi { // please do not cheat the votes lol private const val LOGKEY = "VotingApi" @@ -33,10 +37,13 @@ object VotingApi { // please do not cheat the votes lol } fun SitePlugin.getVoteType(): VoteType { - if (repositoryUrl == null) return VoteType.NONE return getVoteType(url) } + fun SitePlugin.canVote(): Boolean { + return canVote(this.url) + } + // Plugin url to Int private val votesCache = mutableMapOf() @@ -62,7 +69,18 @@ object VotingApi { // please do not cheat the votes lol app.get(url) } + fun canVote(pluginUrl: String): Boolean { + if (!PluginManager.urlPlugins.contains(pluginUrl)) return false + return true + } + suspend fun vote(pluginUrl: String, requestType: VoteType): Int { + if (!canVote(pluginUrl)) { + main { + Toast.makeText(context, R.string.extension_install_first, Toast.LENGTH_SHORT).show() + } + return getVotes(pluginUrl) + } val savedType: VoteType = getKey("cs3-votes/${transformUrl(pluginUrl)}") ?: VoteType.NONE var newType: VoteType = requestType var changeValue = 0 diff --git a/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt b/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt index e7e5b857..f6424c4c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt @@ -13,7 +13,8 @@ class AbstractSubtitleEntities { var epNumber: Int? = null, var seasonNumber: Int? = null, var year: Int? = null, - var isHearingImpaired: Boolean = false + var isHearingImpaired: Boolean = false, + var headers: Map = emptyMap() ) data class SubtitleSearch( diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt index 0d62cbe4..5673e868 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt @@ -4,6 +4,7 @@ import android.util.Log import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.imdbUrlToIdNullable +import com.lagradost.cloudstream3.network.CloudflareKiller import com.lagradost.cloudstream3.subtitles.AbstractSubApi import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities import com.lagradost.cloudstream3.utils.SubtitleHelper @@ -19,6 +20,8 @@ class IndexSubtitleApi : AbstractSubApi { override fun logOut() {} + private val interceptor = CloudflareKiller() + companion object { const val host = "https://indexsubtitle.com" const val TAG = "INDEXSUBS" @@ -122,12 +125,13 @@ class IndexSubtitleApi : AbstractSubApi { type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie, epNumber = epNum, seasonNumber = seasonNum, - year = yearNum + year = yearNum, + headers = interceptor.getCookieHeaders(link).toMap() ) ) } - val document = app.get("$host/?search=$queryText").document + val document = app.get("$host/?search=$queryText", interceptor = interceptor).document document.select("div.my-3.p-3 div.media").map { block -> if (seasonNum > 0) { @@ -159,7 +163,7 @@ class IndexSubtitleApi : AbstractSubApi { val urlItem = fixUrl( it.selectFirst("a")!!.attr("href") ) - val itemDoc = app.get(urlItem).document + val itemDoc = app.get(urlItem, interceptor = interceptor).document val id = imdbUrlToIdNullable( itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() ?.attr("href") @@ -198,7 +202,7 @@ class IndexSubtitleApi : AbstractSubApi { val results = mutableListOf() urlItems.forEach { url -> - val request = app.get(url) + val request = app.get(url, interceptor = interceptor) if (request.isSuccessful) { request.document.select("div.my-3.p-3 div.media").map { block -> if (block.select("span.d-block span[data-original-title=Language]").text() @@ -231,7 +235,7 @@ class IndexSubtitleApi : AbstractSubApi { val seasonNum = data.seasonNumber val epNum = data.epNumber - val req = app.get(data.data) + val req = app.get(data.data, interceptor = interceptor) if (req.isSuccessful) { val document = req.document diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/WebviewFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/WebviewFragment.kt index d773b3a2..cb4bbf37 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/WebviewFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/WebviewFragment.kt @@ -4,10 +4,12 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.webkit.JavascriptInterface import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity import androidx.navigation.fragment.findNavController import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.network.WebViewResolver @@ -46,6 +48,7 @@ class WebviewFragment : Fragment() { return super.shouldOverrideUrlLoading(view, request) } } + web_view.addJavascriptInterface(RepoApi(activity), "RepoApi") web_view.settings.javaScriptEnabled = true web_view.settings.domStorageEnabled = true @@ -69,4 +72,11 @@ class WebviewFragment : Fragment() { putString(WEBVIEW_URL, webViewUrl) } } + + private class RepoApi(val activity: FragmentActivity?) { + @JavascriptInterface + fun installRepo(repoUrl: String) { + activity?.loadRepository(repoUrl) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt index 7cbde29d..ea466120 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/CS3IPlayer.kt @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.ui.SubtitleView import com.google.android.exoplayer2.upstream.DataSource import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory import com.google.android.exoplayer2.upstream.DefaultHttpDataSource +import com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.upstream.cache.CacheDataSource import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor import com.google.android.exoplayer2.upstream.cache.SimpleCache @@ -442,7 +443,14 @@ class CS3IPlayer : IPlayer { var requestSubtitleUpdate: (() -> Unit)? = null - private fun createOnlineSource(link: ExtractorLink): DataSource.Factory { + private fun createOnlineSource(headers: Map): HttpDataSource.Factory { + val source = OkHttpDataSource.Factory(app.baseClient).setUserAgent(USER_AGENT) + return source.apply { + setDefaultRequestProperties(headers) + } + } + + private fun createOnlineSource(link: ExtractorLink): HttpDataSource.Factory { val provider = getApiFromNameNull(link.source) val interceptor = provider?.getVideoInterceptor(link) @@ -813,7 +821,8 @@ class CS3IPlayer : IPlayer { // See setPreferredTextLanguage it.language!!, SubtitleOrigin.EMBEDDED_IN_VIDEO, - it.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP + it.sampleMimeType ?: MimeTypes.APPLICATION_SUBRIP, + emptyMap() ) } @@ -981,9 +990,10 @@ class CS3IPlayer : IPlayer { val mediaItem = getMediaItem(MimeTypes.VIDEO_MP4, data.uri) val offlineSourceFactory = context.createOfflineSource() + val onlineSourceFactory = createOnlineSource(emptyMap()) val (subSources, activeSubtitles) = getSubSources( - onlineSourceFactory = offlineSourceFactory, + onlineSourceFactory = onlineSourceFactory, offlineSourceFactory = offlineSourceFactory, subtitleHelper, ) @@ -997,7 +1007,7 @@ class CS3IPlayer : IPlayer { } private fun getSubSources( - onlineSourceFactory: DataSource.Factory?, + onlineSourceFactory: HttpDataSource.Factory?, offlineSourceFactory: DataSource.Factory?, subHelper: PlayerSubtitleHelper, ): Pair, List> { @@ -1021,7 +1031,10 @@ class CS3IPlayer : IPlayer { SubtitleOrigin.URL -> { if (onlineSourceFactory != null) { activeSubtitles.add(sub) - SingleSampleMediaSource.Factory(onlineSourceFactory) + SingleSampleMediaSource.Factory(onlineSourceFactory.apply { + if (sub.headers.isNotEmpty()) + this.setDefaultRequestProperties(sub.headers) + }) .createMediaSource(subConfig, C.TIME_UNSET) } else { null diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt index 63181d30..baf7ed52 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadFileGenerator.kt @@ -84,7 +84,8 @@ class DownloadFileGenerator( realName.ifBlank { ctx.getString(R.string.default_subtitles) }, file.second.toString(), SubtitleOrigin.DOWNLOADED_FILE, - name.toSubtitleMimeType() + name.toSubtitleMimeType(), + emptyMap() ) ) } 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 e74132bc..c69dc476 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 @@ -399,7 +399,8 @@ class GeneratorPlayer : FullScreenPlayer() { name = getName(currentSubtitle, true), url = url, origin = SubtitleOrigin.URL, - mimeType = url.toSubtitleMimeType() + mimeType = url.toSubtitleMimeType(), + headers = currentSubtitle.headers ) runOnMainThread { addAndSelectSubtitles(subtitle) @@ -483,7 +484,8 @@ class GeneratorPlayer : FullScreenPlayer() { name, uri.toString(), SubtitleOrigin.DOWNLOADED_FILE, - name.toSubtitleMimeType() + name.toSubtitleMimeType(), + emptyMap() ) addAndSelectSubtitles(subtitleData) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt index 142b73ab..b06812c4 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerSubtitleHelper.kt @@ -29,12 +29,14 @@ enum class SubtitleOrigin { /** * @param name To be displayed in the player * @param url Url for the subtitle, when EMBEDDED_IN_VIDEO this variable is used as the real backend language + * @param headers if empty it will use the base onlineDataSource headers else only the specified headers * */ data class SubtitleData( val name: String, val url: String, val origin: SubtitleOrigin, val mimeType: String, + val headers: Map ) class PlayerSubtitleHelper { @@ -71,7 +73,8 @@ class PlayerSubtitleHelper { name = subtitleFile.lang, url = subtitleFile.url, origin = SubtitleOrigin.URL, - mimeType = subtitleFile.url.toSubtitleMimeType() + mimeType = subtitleFile.url.toSubtitleMimeType(), + headers = emptyMap() ) } } 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 b7e36f21..02fe60ca 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 @@ -409,6 +409,11 @@ class ResultViewModel2 : ViewModel() { private const val EPISODE_RANGE_SIZE = 20 private const val EPISODE_RANGE_OVERLOAD = 30 + private fun List?.getSeason(season: Int?): SeasonData? { + if (season == null) return null + return this?.firstOrNull { it.season == season } + } + private fun filterName(name: String?): String? { if (name == null) return null Regex("[eE]pisode [0-9]*(.*)").find(name)?.groupValues?.get(1)?.let { @@ -1476,11 +1481,18 @@ class ResultViewModel2 : ViewModel() { if (isMovie || currentSeasons.size <= 1) null else when (indexer.season) { 0 -> txt(R.string.no_season) - else -> txt( - R.string.season_format, - txt(R.string.season), - indexer.season - ) //TODO FIX DISPLAYNAME + else -> { + val seasonNames = (currentResponse as? EpisodeResponse)?.seasonNames + val seasonData = + seasonNames.getSeason(indexer.season) + val suffix = seasonData?.name?.let { " $it" } ?: "" + txt( + R.string.season_format, + txt(R.string.season), + seasonData?.displaySeason ?: indexer.season, + suffix + ) + } } ) ) @@ -1578,6 +1590,7 @@ class ResultViewModel2 : ViewModel() { val id = mainId + episode + idIndex * 1000000 if (!existingEpisodes.contains(episode)) { existingEpisodes.add(id) + val seasonData = loadResponse.seasonNames.getSeason(i.season) val eps = buildResultEpisode( loadResponse.name, @@ -1585,7 +1598,7 @@ class ResultViewModel2 : ViewModel() { i.posterUrl, episode, null, - i.season, + seasonData?.displaySeason ?: i.season, i.data, loadResponse.apiName, id, @@ -1621,7 +1634,7 @@ class ResultViewModel2 : ViewModel() { existingEpisodes.add(id) val seasonIndex = episode.season?.minus(1) val currentSeason = - loadResponse.seasonNames?.getOrNull(seasonIndex ?: -1) + loadResponse.seasonNames.getSeason(episode.season) val ep = buildResultEpisode( @@ -1630,7 +1643,7 @@ class ResultViewModel2 : ViewModel() { episode.posterUrl, episodeIndex, seasonIndex, - currentSeason?.season ?: episode.season, + currentSeason?.displaySeason ?: episode.season, episode.data, loadResponse.apiName, id, @@ -1731,10 +1744,19 @@ class ResultViewModel2 : ViewModel() { _dubSubSelections.postValue(dubSelection.map { txt(it) to it }) if (loadResponse is EpisodeResponse) { _seasonSelections.postValue(seasonsSelection.map { seasonNumber -> + val seasonData = loadResponse.seasonNames.getSeason(seasonNumber) + val fixedSeasonNumber = seasonData?.displaySeason ?: seasonNumber + val suffix = seasonData?.name?.let { " $it" } ?: "" + val name = /*loadResponse.seasonNames?.firstOrNull { it.season == seasonNumber }?.name?.let { seasonData -> txt(seasonData) - } ?:*/txt(R.string.season_format, txt(R.string.season), seasonNumber) //TODO FIX + } ?:*/txt( + R.string.season_format, + txt(R.string.season), + fixedSeasonNumber, + suffix + ) name to seasonNumber }) } 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 0ae7a258..57074e74 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 @@ -78,6 +78,7 @@ val appLanguages = arrayListOf( Triple("", "Chinese", "zh"), Triple("\uD83C\uDDEE\uD83C\uDDE9", "Indonesian", "in"), Triple("", "Czech", "cs"), + Triple("", "Croatian", "hr"), ).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top class SettingsGeneral : PreferenceFragmentCompat() { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt index d2935358..0c3d481b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginAdapter.kt @@ -183,7 +183,6 @@ class PluginAdapter( }" ) } - } } else { itemView.action_settings?.isVisible = false @@ -217,10 +216,8 @@ class PluginAdapter( itemView.lang_icon.text = "${getFlagFromIso(metadata.language)} ${fromTwoLettersToLanguage(metadata.language)}" } - if (isLocal) { - itemView.ext_votes?.isVisible = false - } else { - itemView.ext_votes?.isVisible = false + itemView.ext_votes?.isVisible = false + if (!isLocal) { ioSafe { metadata.getVotes().main { itemView.ext_votes?.setText(txt(R.string.extension_rating, prettyCount(it))) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt index e3dc31d7..9729b4de 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginDetailsFragment.kt @@ -11,6 +11,8 @@ import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.toPx import kotlinx.android.synthetic.main.fragment_plugin_details.* import android.text.format.Formatter.formatFileSize +import android.util.Log +import androidx.core.view.isVisible import com.lagradost.cloudstream3.plugins.VotingApi import com.lagradost.cloudstream3.plugins.VotingApi.getVoteType import com.lagradost.cloudstream3.plugins.VotingApi.getVotes @@ -19,9 +21,11 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.AcraApplication.Companion.openBrowser -import com.lagradost.cloudstream3.utils.SubtitleHelper +import com.lagradost.cloudstream3.plugins.PluginManager +import com.lagradost.cloudstream3.plugins.VotingApi.canVote import com.lagradost.cloudstream3.utils.SubtitleHelper.fromTwoLettersToLanguage import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso +import kotlinx.android.synthetic.main.repository_item.view.* class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragment() { @@ -80,7 +84,35 @@ class PluginDetailsFragment(val data: PluginViewData) : BottomSheetDialogFragmen if (metadata.repositoryUrl != null) { openBrowser(metadata.repositoryUrl) } + } + if (!metadata.canVote()) { + downvote.alpha = .6f + upvote.alpha = .6f + } + + if (data.isDownloaded) { + // On local plugins page the filepath is provided instead of url. + val plugin = PluginManager.urlPlugins[metadata.url] ?: PluginManager.plugins[metadata.url] + if (plugin?.openSettings != null && context != null) { + action_settings?.isVisible = true + action_settings.setOnClickListener { + try { + plugin.openSettings!!.invoke(requireContext()) + } catch (e: Throwable) { + Log.e( + "PluginAdapter", + "Failed to open ${metadata.name} settings: ${ + Log.getStackTraceString(e) + }" + ) + } + } + } else { + action_settings?.isVisible = false + } + } else { + action_settings?.isVisible = false } upvote.setOnClickListener { diff --git a/app/src/main/res/layout/fragment_plugin_details.xml b/app/src/main/res/layout/fragment_plugin_details.xml index 7b91fe2d..35ab9216 100644 --- a/app/src/main/res/layout/fragment_plugin_details.xml +++ b/app/src/main/res/layout/fragment_plugin_details.xml @@ -43,11 +43,24 @@ android:textStyle="normal" tools:text="Hello world" /> + + diff --git a/app/src/main/res/values-de/strings-de.xml b/app/src/main/res/values-de/strings-de.xml index 4ed1ea72..621080b5 100644 --- a/app/src/main/res/values-de/strings-de.xml +++ b/app/src/main/res/values-de/strings-de.xml @@ -210,7 +210,6 @@ Staffel - %s %d Keine Staffel Episode Episoden diff --git a/app/src/main/res/values-es/array.xml b/app/src/main/res/values-es/array.xml index c040714c..658ba7ae 100644 --- a/app/src/main/res/values-es/array.xml +++ b/app/src/main/res/values-es/array.xml @@ -14,21 +14,6 @@ @id/cast_button_type_forward_30_seconds - - @string/none - Google - Cloudflare - - AdGuard - - - 0 - 1 - 2 - - 4 - - Todos Películas y TV diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml new file mode 100644 index 00000000..bc2a7098 --- /dev/null +++ b/app/src/main/res/values-hr/strings.xml @@ -0,0 +1,547 @@ + + + + %d %s | %s + %s • %s + %s / %s + %s %s + +%d + -%d + %d + %d + %.1f/10.0 + %d + %s Ep %d + Cast: %s + Epizoda %d će izaći + %dd %dh %dm + %dh %dm + %dm + + + Poster + @string/result_poster_img_des + Episode Poster + Main Poster + Next Random + Go back + Change Provider + Preview Background + + + Brzina (%.2fx) + Ocijenjeno: %.1f + Pronađeno novo ažuriranje!\n%s -> %s + Umetak + %d min + + CloudStream + Otvori s CloudStream-om + Početna stranica + Pretraži + Preuzimanja + Postavke + + Pretraži... + Pretraži %s… + + Nema podataka + Više postavki + Sljedeća epizoda + Žanrovi + Podijeli + Otvori u pregledniku + Preskoči učitavanje + Učitavanje... + + Gledam + Na čekanju + Dovršeno + Ispušteno + Planiram pogledati + Ništa + Ponovno gledam + + Pokreni Film + Pokreni LiveStream + Pokreni Torrent + Izvori + Titlovi + Ponovno pokušaj povezivanje… + Idi natrag + Pokreni Epizodu + + + Preuzmi + Preuzeto + Trenutno preuzimam + Preuzimanje pauzirano + Preuzimanje započeto + Preuzimanje nije uspjelo + Preuzimanje otkazano + Preuzimanje dovršeno + Stream + + Pogreška pri učitavanju veza + Unutarnja pohrana + + Dub + Sub + + Izbriši datoteku + Otvori datoteku + Nastavi preuzimanje + Pauziraj preuzimanje + + Onemogući automatsko izvješćivanje o bugovima + Više informacija + Sakrij + Pokreni + Informacije + Filtriraj oznake + Oznake + Makni + Postavi status gledanja + Primijeni + Poništi + Kopiraj + Zatvori + Očisti + Sačuvaj + + Brzina playera + + Postavke titlova + Boja teksta + Boja obruba + Pozadinska boja + Boja prozora + Tip ruba + Visina titlova + Font + Veličina fonta + + Pretraži s uslugama + Pretraži s tipovima + + %d banana dano developerima + Nisi dao ni jednu bananu + + Automatski odabir jezika + Preuzmi jezike + Jezik titlova + Držite za vraćanje na zadane postavke + Uvezi fontove tako da ih postavite u %s + Nastavi gledati + + Makni + Više informacija + @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. + + Opis + Plot nije pronađen + Opis nije pronađen + + Pokaži logmačku 🐈 + + Picture-in-picture + Nastavlja reprodukciju u minijaturnom playeru povrh drugih aplikacija + Gumb za promjenu veličine playera + Ukloni crne rubove + Titlovi + Postavke titlova playera + Chromecast Titlovi + Postavke Chromecast titlova + + Eigengravy način + Dodaje opciju brzine u playeru + Prijeđi prstom za traženje + Prijeđi prstom ulijevo ili udesno za kontrolu vremena u videoplayeru + Klizni za promjenu postavki + Prijeđi prstom ulijevo ili udesno za promjenu svjetline ili glasnoće + + Automatski započni sljedeću epizodu + Započne sljedeću epizodu kad trenutna završi + + Dodirni dvaput za traženje + Dodirni dvaput za pauziranje + Iznos traženja u playeru + Dvaput dodirni desnu ili lijevu stranu ekrana za pomicanje naprijed ili natrag + + Dodirni u sredini za pauziranje + Koristi svijetlinu u sustavu + 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 + + Sigurnosna kopija podataka + Učitana datoteka sigurnosne kopije + Vraćanje podataka iz datoteke nije uspjelo %s + Uspješno pohranjeni podaci + Nedostaju dozvole za pohranu, pokušaj ponovo + Pogreška pri sigurnosnom kopiranju %s + + Pretraži + Računi + Ažuriranja i sigurnosne kopije + + Informacije + Napredno pretraživanje + Daje rezultate pretraživanja odvojene prema pružatelju usluga + Šalje samo podatke o padovima aplikacije + Ne šalje podatke + Prikaži dodatnu epizodu za anime + Prikaži trailere + Prikaži postere iz kitsua + Sakrij odabranu kvalitetu videozapisa u rezultatima pretraživanja + + Automatsko ažuriranje dodataka + Prikaži ažuriranja aplikacije + Automatski traži nova ažuriranja pri pokretanju aplikacije + Ažuriranje na predizdanja + Tražite ažuriranja prije izdanja umjesto samo potpunih izdanja + Github + Aplikacija za romane od istih developera + Anime aplikacija od istih developera + Uđi u naš Discord + Daj bananu developerima + Dana banana + + Jezik aplikacije + + Ovaj pružatelj usluga nema podršku za Chromecast + Nisu pronađene veze + 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 + + + Sezona + Nema sezone + Epizoda + Epizode + %d-%d + %d %s + S + E + Nisu pronađene epizode + + Izbriši datoteku + Izbriši + Pauziraj + Nastavi + -30 + +30 + Ovo će trajno izbrisati %s\nJeste li sigurni? + %dm\npreostalo + + + U tijeku + Završeno + Status + Godina + Ocjena + Trajanje + Stranica + Sinopsis + + u redu čekanja + Nema titlova + Zadano + + Slobodno + Iskorišteno + Aplikacija + + + Filmovi + TV Serije + Crtići + Anime + Torrenti + Dokumentarci + OVA + Azijske drame + Livestreamovi + NSFW + Ostali + + + Film + Serija + Crtić + @string/anime + @string/ova + Torrent + Dokumentarac + Azijska drama + Livestream + NSFW + Video + + Greška u izvoru + Pogreška remote-a + Pogreška renderera + Neočekivana pogreška playera + Pogreška preuzimanja, provjeri dozvole za pohranu + + Chromecast epizoda + Chromecast mirror + Pokreni u aplikaciji + Pokreni u VLC-u + Pokreni u pregledniku + Kopiraj poveznicu + Automatsko preuzimanje + Preuzmi zrcalo + Ponovno učitaj poveznice + Preuzmi titlove + + Oznaka kvalitete + Oznaka sinkronizacije + Oznaka titlova + Naslov + Uključi/isključi elemente korisničkog sučelja na posteru + + Nije pronađeno ažuriranje + Provjeri ažuriranja + + Zaključaj + Promijeni veličinu + Izvor + Preskoči OP + + Ne prikazuj više + Preskoči ovo ažuriranje + Ažuriraj + Preferirana kvaliteta streama + Maksimalni broj znakova u naslovu video playera + Rezolucija video playera + + Veličina video međuspremnika + Duljina video međuspremnika + Video predmemorija na disku + Očisti predmemoriju videa i slika + + Izazvat će nasumična rušenja ako se postavi previsoko. Nemojte mijenjati ako imate malu količinu RAM-a kao što je Android TV ili stari telefon + Može uzrokovati probleme na sustavima s malo prostora za pohranu kao što su Android TV uređaji ako postavite previsoko + + DNS preko HTTPS-a + Korisno za zaobilaženje blokada ISP-a + + Kloniraj web stranicu + Ukloni web stranicu + Dodajt klon postojeće web-lokacije s drugim url-om + + Put preuzimanja + + Nginx server url + + Prikaži sinkronizirani anime ili s titlovima + + Prilagodi zaslonu + Rastegni + Zoom + + Odricanje + 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 + Jezici pružatelja usluga + Izgled aplikacije + Preferirani mediji + Omogući NSFW na podržanim pružateljima usluga + Kodiranje titlova + Pružatelji usluga + Izgled + + Auto + TV izgled + Izgled za telefone + Izgled za emulatore + + Primarna boja + Tema aplikacije + Mjesto naslova postera + Stavi naslov ispod postera + + + + lozinka123 + MojeCoolIme + bok@svijete.com + 127.0.0.1 + MojaCoolStranica + primjer.com + Šifra jezika (en) + + %s %s + račun + Odjava + Prijava + Promijeni račun + Dodaj račun + Napravi račun + Dodaj tracking + Dodano %s + Sinkroniziraj + Ocijenjeno + %d / 10 + /?? + /%d + Autentičnost %s + Provjera autentičnosti nije uspjela %s + + + Nijedan + Normal + Sve + Maksimalno + Minimalno + Obrub + Depresivno + Sjena + Podignuto + Sinkroniziraj titlove + 1000ms + Kašnjenje titlova + Koristi ovo ako su titlovi prikazani %dms prerano + Koristite ovo ako se titlovi prikazuju %dms prekasno + Nema kašnjenja titlova + + + The quick brown fox jumps over the lazy dog + + Preporučeno + Učitano %s + Učitaj iz datoteke + Učitaj sa interneta + Preuzeta datoteka + Glavno + Podupiranje + Pozadina + + Izvor + Random + + Dolazi uskoro… + + Cam + Cam + Cam + HQ + HD + TS + TC + BlueRay + WP + DVD + 4K + SD + UHD + HDR + SDR + Web + + Slika postera + Player + Rezolucija i naslov + Naslov + Rezolucija + Nevažeći ID + Nevažeći podaci + Nevažeći URL + Greška + Ukloni titlove iz titlova + Ukloni reklame iz titlova + Filtriraj po željenom jeziku medija + Extras + Trailer + Veza na stream + Upućivač + Sljedeće + Gledaj videozapise na ovim jezicima + Prethodno + Preskoči postavljanje + Promijeni izgled aplikacije kako bi odgovarao vašem uređaju + Izvještavanje o rušenju + Što želite vidjeti + Gotovo + Ekstenzije + Dodaj repository + Ime repositorya + URL Repositorija + Dodatak je učitan + Dodatak je izbrisan + Učitavanje nije uspjelo %s + 18+ + Započelo preuzimanje %d %s + Preuzeto %d %s uspješno + Sve %s je već preuzeto + Skupno preuzimanje + plugin + plugins + Ovo će također izbrisati sve dodatke spremišta + Izbriši repository + Preuzmi popis stranica koje želite koristiti + Preuzeto: %d + Onemogućeno: %d + Nije preuzeto: %d + Dodaj spremište za instaliranje ekstenzija web mjesta + Pregledajte repozitorije zajednice + Javni popis + Svi titlovi pisani velikim slovima + + Preuzeti sve dodatke iz ovog repozitorija? + %s (Onemogućeno) + Zapis + Audio zapis + Video zapis + Primijeni na restart + + Siguran način rada omogućen + Došlo je do nepopravljivog pada i automatski smo onemogućili sva proširenja, tako da možete pronaći i ukloniti proširenje koje uzrokuje probleme. + Pogledajte podatke o padu + + Ocjena: %s + Opis + Verzija + Status + Veličina + Autori + Podržano + Jezik + + HLS Playlista + diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index ca970d31..ae774f03 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -208,7 +208,6 @@ Sorry, de applicatie is gecrasht. Er wordt een anoniem bugrapport naar de ontwikkelaars gestuurd Seizoen - %s %d Geen seizoen Aflevering afleveringen diff --git a/app/src/main/res/values-pl/array.xml b/app/src/main/res/values-pl/array.xml index a251a41b..c40b7f87 100644 --- a/app/src/main/res/values-pl/array.xml +++ b/app/src/main/res/values-pl/array.xml @@ -14,21 +14,6 @@ @id/cast_button_type_forward_30_seconds - - @string/none - Google - Cloudflare - - AdGuard - - - 0 - 1 - 2 - - 4 - - Nie działa diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 55789920..ea6eb140 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -454,4 +454,5 @@ Autorzy Wspierane Język + Najpierw zainstaluj rozszerzenie diff --git a/app/src/main/res/values-tr/array.xml b/app/src/main/res/values-tr/array.xml index 1ec2e27e..ec9acd0e 100644 --- a/app/src/main/res/values-tr/array.xml +++ b/app/src/main/res/values-tr/array.xml @@ -14,21 +14,6 @@ @id/cast_button_type_forward_30_seconds - - @string/none - Google - Cloudflare - - AdGuard - - - 0 - 1 - 2 - - 4 - - Hepsi Film ve Dizi diff --git a/app/src/main/res/values-vi/array.xml b/app/src/main/res/values-vi/array.xml index 197aac78..5e70223b 100644 --- a/app/src/main/res/values-vi/array.xml +++ b/app/src/main/res/values-vi/array.xml @@ -14,21 +14,6 @@ @id/cast_button_type_forward_30_seconds - - @string/none - Google - Cloudflare - - AdGuard - - - 0 - 1 - 2 - - 4 - - Tất cả Phim lẻ và Phim bộ diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 4f6fb7b8..302c13b5 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -221,7 +221,6 @@ Mùa - %s %d Không có mùa nào Tập Tập diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 09411075..d3ff6e1e 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -231,7 +231,6 @@ - %s %d 无季 diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index acfd1ee9..13d4f2dc 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -20,6 +20,8 @@ Cloudflare AdGuard + DNS.WATCH + Quad9 0 @@ -27,6 +29,8 @@ 2 4 + 5 + 6 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30efa924..8e6dadcf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -290,7 +290,7 @@ Season - %s %d + %s %d%s No Season Episode Episodes @@ -624,6 +624,7 @@ Authors Supported Language + Install the extension first HLS Playlist