From c07e6d3222123ce9b711cafa8827f682f9ad9516 Mon Sep 17 00:00:00 2001 From: int3debug <164035730+int3debug@users.noreply.github.com> Date: Thu, 2 May 2024 23:58:32 +0200 Subject: [PATCH 01/24] hotfix: Remove resume information (#1063) --- .../cloudstream3/ui/download/button/PieFetchButton.kt | 4 ++++ .../com/lagradost/cloudstream3/utils/VideoDownloadManager.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt index a729f33a..f1031c24 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/button/PieFetchButton.kt @@ -13,6 +13,8 @@ import androidx.annotation.MainThread import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isVisible +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DELETE_FILE @@ -25,6 +27,7 @@ import com.lagradost.cloudstream3.ui.download.DownloadClickEvent import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIcons import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager +import com.lagradost.cloudstream3.utils.VideoDownloadManager.KEY_RESUME_PACKAGES open class PieFetchButton(context: Context, attributeSet: AttributeSet) : @@ -167,6 +170,7 @@ open class PieFetchButton(context: Context, attributeSet: AttributeSet) : this.setPersistentId(card.id) view.setOnClickListener { if (isZeroBytes) { + removeKey(KEY_RESUME_PACKAGES, card.id.toString()) callback(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, card)) //callback.invoke(DownloadClickEvent(DOWNLOAD_ACTION_DOWNLOAD, data)) } else { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt index 50a8df02..7d4d5d98 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/VideoDownloadManager.kt @@ -187,7 +187,7 @@ object VideoDownloadManager { private val DOWNLOAD_BAD_CONFIG = DownloadStatus(retrySame = false, tryNext = false, success = false) - private const val KEY_RESUME_PACKAGES = "download_resume" + const val KEY_RESUME_PACKAGES = "download_resume" const val KEY_DOWNLOAD_INFO = "download_info" private const val KEY_RESUME_QUEUE_PACKAGES = "download_q_resume" From d3828eeafed0fd4fbeb32c4d37dee2126296b564 Mon Sep 17 00:00:00 2001 From: int3debug <164035730+int3debug@users.noreply.github.com> Date: Thu, 2 May 2024 23:59:05 +0200 Subject: [PATCH 02/24] refact: rename logcat file (#1061) Rename logcat file to prevent override --- .../cloudstream3/ui/settings/SettingsUpdates.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt index fb24c185..4aaa5e12 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt @@ -35,6 +35,9 @@ import okhttp3.internal.closeQuietly import java.io.BufferedReader import java.io.InputStreamReader import java.io.OutputStream +import java.lang.System.currentTimeMillis +import java.text.SimpleDateFormat +import java.util.* class SettingsUpdates : PreferenceFragmentCompat() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -125,12 +128,12 @@ class SettingsUpdates : PreferenceFragmentCompat() { } binding.saveBtt.setOnClickListener { + val date = SimpleDateFormat("yyyy_MM_dd_HH_mm").format(Date(currentTimeMillis())) var fileStream: OutputStream? = null try { - fileStream = - VideoDownloadManager.setupStream( + fileStream = VideoDownloadManager.setupStream( it.context, - "logcat", + "logcat_${date}", null, "txt", false From c28a3cb9873d64634b1e7bb131ef648ab40fd22e Mon Sep 17 00:00:00 2001 From: RowdyRushya <66415100+rushi-chavan@users.noreply.github.com> Date: Sat, 4 May 2024 04:15:34 -0700 Subject: [PATCH 03/24] Extractor: new VidSrcTo extractor (#1044) --- .../cloudstream3/extractors/VidSrcTo.kt | 65 +++++++++++++++++++ .../cloudstream3/extractors/Vidplay.kt | 4 ++ .../metaproviders/TmdbProvider.kt | 2 + .../cloudstream3/utils/ExtractorApi.kt | 2 + 4 files changed, 73 insertions(+) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcTo.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcTo.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcTo.kt new file mode 100644 index 00000000..b9065688 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/VidSrcTo.kt @@ -0,0 +1,65 @@ +package com.lagradost.cloudstream3.extractors + +import android.util.Base64 +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.amap +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.utils.ExtractorApi +import com.lagradost.cloudstream3.utils.ExtractorLink +import java.net.URLDecoder +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec + +class VidSrcTo : ExtractorApi() { + override val name = "VidSrcTo" + override val mainUrl = "https://vidsrc.to" + override val requiresReferer = true + + override suspend fun getUrl( + url: String, + referer: String?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val mediaId = app.get(url).document.selectFirst("ul.episodes li a")?.attr("data-id") ?: return + val res = app.get("$mainUrl/ajax/embed/episode/$mediaId/sources").parsedSafe() ?: return + if (res.status != 200) return + res.result?.amap { source -> + val embedRes = app.get("$mainUrl/ajax/embed/source/${source.id}").parsedSafe() ?: return@amap + val finalUrl = DecryptUrl(embedRes.result.encUrl) + if(finalUrl.equals(embedRes.result.encUrl)) return@amap + when (source.title) { + "Vidplay" -> AnyVidplay(finalUrl.substringBefore("/e/")).getUrl(finalUrl, referer, subtitleCallback, callback) + "Filemoon" -> FileMoon().getUrl(finalUrl, referer, subtitleCallback, callback) + } + } + } + + private fun DecryptUrl(encUrl: String): String { + var data = encUrl.toByteArray() + data = Base64.decode(data, Base64.URL_SAFE) + val rc4Key = SecretKeySpec("WXrUARXb1aDLaZjI".toByteArray(), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters) + data = cipher.doFinal(data) + return URLDecoder.decode(data.toString(Charsets.UTF_8), "utf-8") + } + + data class VidsrctoEpisodeSources( + @JsonProperty("status") val status: Int, + @JsonProperty("result") val result: List? + ) + + data class VidsrctoResult( + @JsonProperty("id") val id: String, + @JsonProperty("title") val title: String + ) + + data class VidsrctoEmbedSource( + @JsonProperty("status") val status: Int, + @JsonProperty("result") val result: VidsrctoUrl + ) + + data class VidsrctoUrl(@JsonProperty("url") val encUrl: String) +} diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt index d5d0fb32..c5e01552 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vidplay.kt @@ -13,6 +13,10 @@ import javax.crypto.spec.SecretKeySpec // Code found in https://github.com/KillerDogeEmpire/vidplay-keys // special credits to @KillerDogeEmpire for providing key +class AnyVidplay(hostUrl: String) : Vidplay() { + override val mainUrl = hostUrl +} + class MyCloud : Vidplay() { override val name = "MyCloud" override val mainUrl = "https://mcloud.bz" diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt index 50301e22..c5b4d453 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt @@ -105,6 +105,7 @@ open class TmdbProvider : MainAPI() { this.id, episode.episode_number, episode.season_number, + this.name ?: this.original_name, ).toJson(), episode.name, episode.season_number, @@ -122,6 +123,7 @@ open class TmdbProvider : MainAPI() { this.id, episodeNum, season.season_number, + this.name ?: this.original_name, ).toJson(), season = season.season_number ) 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 75dceb54..6106845e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -185,6 +185,7 @@ import com.lagradost.cloudstream3.extractors.Vanfem import com.lagradost.cloudstream3.extractors.Vicloud import com.lagradost.cloudstream3.extractors.VidSrcExtractor import com.lagradost.cloudstream3.extractors.VidSrcExtractor2 +import com.lagradost.cloudstream3.extractors.VidSrcTo import com.lagradost.cloudstream3.extractors.VideoVard import com.lagradost.cloudstream3.extractors.VideovardSX import com.lagradost.cloudstream3.extractors.Vidgomunime @@ -876,6 +877,7 @@ val extractorApis: MutableList = arrayListOf( Streamlare(), VidSrcExtractor(), VidSrcExtractor2(), + VidSrcTo(), PlayLtXyz(), AStreamHub(), Vidplay(), From 83c473d9f801cc43c0716453bea79afc539a1fea Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 4 May 2024 14:16:09 +0300 Subject: [PATCH 04/24] More external Ids in Trakt meta provider (#1075) --- .../cloudstream3/metaproviders/TraktProvider.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt index 98e12bcd..37c6be1b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt @@ -118,8 +118,12 @@ open class TraktProvider : MainAPI() { val linkData = LinkData( id = mediaDetails?.ids?.tmdb, + traktId = mediaDetails?.ids?.trakt, + traktSlug = mediaDetails?.ids?.slug, + tmdbId = mediaDetails?.ids?.tmdb, imdbId = mediaDetails?.ids?.imdb.toString(), tvdbId = mediaDetails?.ids?.tvdb, + tvrageId = mediaDetails?.ids?.tvrage, type = data.type.toString(), title = mediaDetails?.title, year = mediaDetails?.year, @@ -139,7 +143,6 @@ open class TraktProvider : MainAPI() { type = if (isAnime) TvType.AnimeMovie else TvType.Movie, ) { this.name = mediaDetails.title - this.apiName = "Trakt" this.type = if (isAnime) TvType.AnimeMovie else TvType.Movie this.posterUrl = getOriginalWidthImageUrl(posterUrl) this.year = mediaDetails.year @@ -177,8 +180,12 @@ open class TraktProvider : MainAPI() { val linkData = LinkData( id = mediaDetails?.ids?.tmdb, + traktId = mediaDetails?.ids?.trakt, + traktSlug = mediaDetails?.ids?.slug, + tmdbId = mediaDetails?.ids?.tmdb, imdbId = mediaDetails?.ids?.imdb.toString(), tvdbId = mediaDetails?.ids?.tvdb, + tvrageId = mediaDetails?.ids?.tvrage, type = data.type.toString(), season = episode.season, episode = episode.number, @@ -220,7 +227,6 @@ open class TraktProvider : MainAPI() { episodes = episodes ) { this.name = mediaDetails.title - this.apiName = "Trakt" this.type = if (isAnime) TvType.Anime else TvType.TvSeries this.episodes = episodes this.posterUrl = getOriginalWidthImageUrl(posterUrl) @@ -406,8 +412,12 @@ open class TraktProvider : MainAPI() { data class LinkData( val id: Int? = null, + val traktId: Int? = null, + val traktSlug: String? = null, + val tmdbId: Int? = null, val imdbId: String? = null, val tvdbId: Int? = null, + val tvrageId: String? = null, val type: String? = null, val season: Int? = null, val episode: Int? = null, From 71bd48f4930d255beabe6f86b7e4057b732dc70e Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 4 May 2024 14:17:52 +0300 Subject: [PATCH 05/24] feat(ui): Hide Downloads & Settings Back button on TV (#1074) --- .../ui/download/DownloadChildFragment.kt | 11 ++++++++--- .../ui/quicksearch/QuickSearchFragment.kt | 12 ++++++++++-- .../ui/settings/SettingsFragment.kt | 19 ++++++++++++------- app/src/main/res/layout/quick_search.xml | 7 +++---- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt index d138a1e6..f54c8698 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadChildFragment.kt @@ -11,6 +11,9 @@ import com.lagradost.cloudstream3.databinding.FragmentChildDownloadsBinding import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick import com.lagradost.cloudstream3.ui.result.FOCUS_SELF import com.lagradost.cloudstream3.ui.result.setLinearListLayout +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE +import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKeys @@ -86,9 +89,11 @@ class DownloadChildFragment : Fragment() { binding?.downloadChildToolbar?.apply { title = name - setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) - setNavigationOnClickListener { - activity?.onBackPressedDispatcher?.onBackPressed() + if (isLayout(PHONE or EMULATOR)) { + setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) + setNavigationOnClickListener { + activity?.onBackPressedDispatcher?.onBackPressed() + } } setAppBarNoScrollFlagsOnTV() } 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 e9e00736..85e20d1c 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 @@ -34,6 +34,9 @@ import com.lagradost.cloudstream3.ui.search.SearchAdapter import com.lagradost.cloudstream3.ui.search.SearchClickCallback import com.lagradost.cloudstream3.ui.search.SearchHelper import com.lagradost.cloudstream3.ui.search.SearchViewModel +import com.lagradost.cloudstream3.ui.settings.Globals +import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.AppUtils.ownShow @@ -274,8 +277,13 @@ class QuickSearchFragment : Fragment() { // UIHelper.showInputMethod(view.findFocus()) // } //} - binding?.quickSearchBack?.setOnClickListener { - activity?.popCurrentPage() + if (isLayout(PHONE or EMULATOR)) { + binding?.quickSearchBack?.apply { + isVisible = true + setOnClickListener { + activity?.popCurrentPage() + } + } } if (isLayout(TV)) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index 443eeda7..8ac17928 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -23,6 +23,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.account import com.lagradost.cloudstream3.ui.home.HomeFragment import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR +import com.lagradost.cloudstream3.ui.settings.Globals.PHONE import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.isLayout import com.lagradost.cloudstream3.utils.DataStoreHelper @@ -84,9 +85,11 @@ class SettingsFragment : Fragment() { settingsToolbar.apply { setTitle(title) - setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) - setNavigationOnClickListener { - activity?.onBackPressedDispatcher?.onBackPressed() + if (isLayout(PHONE or EMULATOR)) { + setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) + setNavigationOnClickListener { + activity?.onBackPressedDispatcher?.onBackPressed() + } } } UIHelper.fixPaddingStatusbar(settingsToolbar) @@ -98,10 +101,12 @@ class SettingsFragment : Fragment() { settingsToolbar.apply { setTitle(title) - setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) - children.firstOrNull { it is ImageView }?.tag = getString(R.string.tv_no_focus_tag) - setNavigationOnClickListener { - activity?.onBackPressedDispatcher?.onBackPressed() + if (isLayout(PHONE or EMULATOR)) { + setNavigationIcon(R.drawable.ic_baseline_arrow_back_24) + children.firstOrNull { it is ImageView }?.tag = getString(R.string.tv_no_focus_tag) + setNavigationOnClickListener { + activity?.onBackPressedDispatcher?.onBackPressed() + } } } UIHelper.fixPaddingStatusbar(settingsToolbar) diff --git a/app/src/main/res/layout/quick_search.xml b/app/src/main/res/layout/quick_search.xml index 12d94aaa..84f2c548 100644 --- a/app/src/main/res/layout/quick_search.xml +++ b/app/src/main/res/layout/quick_search.xml @@ -23,11 +23,10 @@ android:background="?android:attr/selectableItemBackgroundBorderless" android:src="@drawable/ic_baseline_arrow_back_24" app:tint="@android:color/white" - android:focusable="true" + android:visibility="gone" android:layout_width="25dp" - android:layout_height="wrap_content"> - - + android:layout_height="wrap_content" + tools:visibility="visible"> Date: Sun, 5 May 2024 04:30:42 +0530 Subject: [PATCH 06/24] Updates and Chillx Extractor Updated (#1065) --- .../cloudstream3/extractors/Chillx.kt | 48 ++++++++++--------- .../cloudstream3/extractors/EPlay.kt | 1 - .../lagradost/cloudstream3/extractors/Vtbe.kt | 1 - 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Chillx.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Chillx.kt index f03a5525..26567c7a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Chillx.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Chillx.kt @@ -2,9 +2,7 @@ package com.lagradost.cloudstream3.extractors import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.extractors.helper.* import com.lagradost.cloudstream3.extractors.helper.AesHelper.cryptoAESHandler -import com.lagradost.cloudstream3.utils.AppUtils import com.lagradost.cloudstream3.utils.ExtractorApi import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper @@ -28,30 +26,39 @@ open class Chillx : ExtractorApi() { override val name = "Chillx" override val mainUrl = "https://chillx.top" override val requiresReferer = true - private var key: String? = null + companion object { + private var key: String? = null + + suspend fun fetchKey(): String { + return if (key != null) { + key!! + } else { + val fetch = app.get("https://raw.githubusercontent.com/rushi-chavan/multi-keys/keys/keys.json").parsedSafe()?.key?.get(0) ?: throw ErrorLoadingException("Unable to get key") + key = fetch + key!! + } + } + } + + @Suppress("NAME_SHADOWING") override suspend fun getUrl( url: String, referer: String?, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { - val master = Regex("\\s*=\\s*'([^']+)").find( + val master = Regex("""JScript[\w+]?\s*=\s*'([^']+)""").find( app.get( url, - referer = referer ?: "", - headers = mapOf( - "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", - "Accept-Language" to "en-US,en;q=0.5", - ) + referer = url, ).text )?.groupValues?.get(1) - val decrypt = cryptoAESHandler(master ?: return, getKey().toByteArray(), false)?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") - + val key = fetchKey() + val decrypt = cryptoAESHandler(master ?: "", key.toByteArray(), false)?.replace("\\", "") ?: throw ErrorLoadingException("failed to decrypt") val source = Regex(""""?file"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - val subtitles = Regex("""subtitle"?:\s*"([^"]+)""").find(decrypt)?.groupValues?.get(1) - val subtitlePattern = """\[(.*?)\](https?://[^\s,]+)""".toRegex() + val subtitlePattern = """\[(.*?)](https?://[^\s,]+)""".toRegex() val matches = subtitlePattern.findAll(subtitles ?: "") val languageUrlPairs = matches.map { matchResult -> val (language, url) = matchResult.destructured @@ -83,23 +90,18 @@ open class Chillx : ExtractorApi() { headers = headers ).forEach(callback) } - + private fun decodeUnicodeEscape(input: String): String { val regex = Regex("u([0-9a-fA-F]{4})") return regex.replace(input) { it.groupValues[1].toInt(16).toChar().toString() } } - - suspend fun getKey() = key ?: fetchKey().also { key = it } - private suspend fun fetchKey(): String { - return app.get("https://raw.githubusercontent.com/Sofie99/Resources/main/chillix_key.json").parsed() - } - data class Tracks( - @JsonProperty("file") val file: String? = null, - @JsonProperty("label") val label: String? = null, - @JsonProperty("kind") val kind: String? = null, + + data class Keys( + @JsonProperty("chillx") val key: List ) + } diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/EPlay.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/EPlay.kt index 565a2680..2cb12e16 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/EPlay.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/EPlay.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.extractors -import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vtbe.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vtbe.kt index 65af01ec..919a9cbd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Vtbe.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Vtbe.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream3.extractors -import android.util.Log import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.utils.* From 3874cb9f9d3a2b0894c59346b92fb6eec4fa2b2e Mon Sep 17 00:00:00 2001 From: b4byhuey <60543438+b4byhuey@users.noreply.github.com> Date: Thu, 9 May 2024 23:06:33 +0800 Subject: [PATCH 07/24] Update Dailymotion Extractor (#1081) --- .../cloudstream3/extractors/Dailymotion.kt | 23 +++++++++++++------ .../cloudstream3/utils/ExtractorApi.kt | 5 +++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt index 0df93dc5..2343a92e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/Dailymotion.kt @@ -9,10 +9,16 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.M3u8Helper.Companion.generateM3u8 import java.net.URL +class Geodailymotion : Dailymotion() { + override val name = "GeoDailymotion" + override val mainUrl = "https://geo.dailymotion.com" +} + open class Dailymotion : ExtractorApi() { override val mainUrl = "https://www.dailymotion.com" override val name = "Dailymotion" override val requiresReferer = false + private val baseUrl = "https://www.dailymotion.com" @Suppress("RegExpSimplifiable") private val videoIdRegex = "^[kx][a-zA-Z0-9]+\$".toRegex() @@ -34,7 +40,7 @@ open class Dailymotion : ExtractorApi() { val dmV1st = config.dmInternalData.v1st val dmTs = config.dmInternalData.ts val embedder = config.context.embedder - val metaDataUrl = "$mainUrl/player/metadata/video/$id?embedder=$embedder&locale=en-US&dmV1st=$dmV1st&dmTs=$dmTs&is_native_app=0" + val metaDataUrl = "$baseUrl/player/metadata/video/$id?embedder=$embedder&locale=en-US&dmV1st=$dmV1st&dmTs=$dmTs&is_native_app=0" val metaData = app.get(metaDataUrl, referer = embedUrl, cookies = req.cookies) .parsedSafe() ?: return metaData.qualities.forEach { (_, video) -> @@ -45,16 +51,19 @@ open class Dailymotion : ExtractorApi() { } private fun getEmbedUrl(url: String): String? { - if (url.contains("/embed/")) { - return url - } - val vid = getVideoId(url) ?: return null - return "$mainUrl/embed/video/$vid" + if (url.contains("/embed/") || url.contains("/video/")) { + return url } + if (url.contains("geo.dailymotion.com")) { + val videoId = url.substringAfter("video=") + return "$baseUrl/embed/video/$videoId" + } + return null + } private fun getVideoId(url: String): String? { val path = URL(url).path - val id = path.substringAfter("video/") + val id = path.substringAfter("/video/") if (id.matches(videoIdRegex)) { return id } 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 6106845e..0e4dc870 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -53,6 +53,7 @@ import com.lagradost.cloudstream3.extractors.FileMoonIn import com.lagradost.cloudstream3.extractors.FileMoonSx import com.lagradost.cloudstream3.extractors.Filesim import com.lagradost.cloudstream3.extractors.Fplayer +import com.lagradost.cloudstream3.extractors.Geodailymotion import com.lagradost.cloudstream3.extractors.GMPlayer import com.lagradost.cloudstream3.extractors.Gdriveplayer import com.lagradost.cloudstream3.extractors.Gdriveplayerapi @@ -900,7 +901,9 @@ val extractorApis: MutableList = arrayListOf( Simpulumlamerop(), Urochsunloath(), Yipsu(), - MetaGnathTuggers() + MetaGnathTuggers(), + Geodailymotion(), + ) From f1cc4db89cc6c1a2cd6316e81340206b56a72ad6 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Thu, 9 May 2024 18:08:18 +0300 Subject: [PATCH 08/24] Show Season number for next airing episode (#1071) --- .../com/lagradost/cloudstream3/MainAPI.kt | 17 +++++++-- .../ui/result/ResultViewModel2.kt | 6 +++- .../main/res/layout/fragment_result_tv.xml | 36 +++++++++---------- app/src/main/res/values/strings.xml | 1 + 4 files changed, 38 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 7b1b5775..699159b5 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -1448,11 +1448,24 @@ fun TvType?.isEpisodeBased(): Boolean { return (this == TvType.TvSeries || this == TvType.Anime || this == TvType.AsianDrama) } - data class NextAiring( val episode: Int, val unixTime: Long, -) + val season: Int? = null, +) { + /** + * Secondary constructor for backwards compatibility without season. + * TODO Remove this constructor after there is a new stable release and extensions are updated to support season. + */ + constructor( + episode: Int, + unixTime: Long, + ) : this ( + episode, + unixTime, + null + ) +} /** * @param season To be mapped with episode season, not shown in UI if displaySeason is defined 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 de339aee..61b65bc2 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 @@ -197,7 +197,11 @@ fun LoadResponse.toResultData(repo: APIRepository): ResultData { else -> null }?.also { - nextAiringEpisode = txt(R.string.next_episode_format, airing.episode) + nextAiringEpisode = when (airing.season) { + + null -> txt(R.string.next_episode_format, airing.episode) + else -> txt(R.string.next_season_episode_format, airing.season, airing.episode) + } } } } diff --git a/app/src/main/res/layout/fragment_result_tv.xml b/app/src/main/res/layout/fragment_result_tv.xml index 2ec2ae0a..893c19ff 100644 --- a/app/src/main/res/layout/fragment_result_tv.xml +++ b/app/src/main/res/layout/fragment_result_tv.xml @@ -178,42 +178,40 @@ https://developer.android.com/design/ui/tv/samples/jet-fit android:textStyle="bold" tools:text="The Perfect Run The Perfect Run" /> + + - - + android:orientation="horizontal"> + android:layout_marginEnd="5dp" + tools:text="Season 2 Episode 1022 will be released in" /> %1$s Ep %2$d Cast: %s Episode %d will be released in + Season %1$d Episode %2$d will be released in %1$dd %2$dh %3$dm %1$dh %2$dm %dm From ee4d1dedc5adb1be656a05d1ee0f41b11f9d0a84 Mon Sep 17 00:00:00 2001 From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com> Date: Thu, 9 May 2024 19:46:54 +0000 Subject: [PATCH 09/24] Add basic fcast support (#1084) --- .../lagradost/cloudstream3/MainActivity.kt | 3 + .../cloudstream3/ui/player/IGenerator.kt | 10 +- .../cloudstream3/ui/result/EpisodeAdapter.kt | 2 + .../ui/result/ResultViewModel2.kt | 44 ++++++ .../cloudstream3/utils/ExtractorApi.kt | 13 +- .../cloudstream3/utils/fcast/FcastManager.kt | 135 ++++++++++++++++++ .../cloudstream3/utils/fcast/FcastSession.kt | 60 ++++++++ .../cloudstream3/utils/fcast/Packets.kt | 62 ++++++++ app/src/main/res/values/strings.xml | 3 + 9 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 7baac71c..56322b73 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -161,6 +161,7 @@ import com.lagradost.cloudstream3.utils.UIHelper.requestRW import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.USER_PROVIDER_API import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API +import com.lagradost.cloudstream3.utils.fcast.FcastManager import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import com.lagradost.safefile.SafeFile @@ -1756,6 +1757,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, runAutoUpdate() } + FcastManager().init(this, false) + APIRepository.dubStatusActive = getApiDubstatusSettings() try { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt index af74cb57..c5de1a1c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IGenerator.kt @@ -10,7 +10,8 @@ enum class LoadType { InAppDownload, ExternalApp, Browser, - Chromecast + Chromecast, + Fcast } fun LoadType.toSet() : Set { @@ -29,12 +30,17 @@ fun LoadType.toSet() : Set { ExtractorLinkType.VIDEO, ExtractorLinkType.M3U8 ) - LoadType.ExternalApp, LoadType.Unknown -> ExtractorLinkType.values().toSet() + LoadType.ExternalApp, LoadType.Unknown -> ExtractorLinkType.entries.toSet() LoadType.Chromecast -> setOf( ExtractorLinkType.VIDEO, ExtractorLinkType.DASH, ExtractorLinkType.M3U8 ) + LoadType.Fcast -> setOf( + ExtractorLinkType.VIDEO, + ExtractorLinkType.DASH, + ExtractorLinkType.M3U8 + ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt index 2019aa50..e4fd0559 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/EpisodeAdapter.kt @@ -55,6 +55,8 @@ const val ACTION_PLAY_EPISODE_IN_WEB_VIDEO = 16 const val ACTION_PLAY_EPISODE_IN_MPV = 17 const val ACTION_MARK_AS_WATCHED = 18 +const val ACTION_FCAST = 19 + const val TV_EP_SIZE_LARGE = 400 const val TV_EP_SIZE_SMALL = 300 data class EpisodeClickEvent(val action: Int, val data: ResultEpisode) 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 61b65bc2..a32942f6 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 @@ -83,6 +83,10 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper.setVideoWatchState import com.lagradost.cloudstream3.utils.DataStoreHelper.updateSubscribedData import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper import com.lagradost.cloudstream3.utils.UIHelper.navigate +import com.lagradost.cloudstream3.utils.fcast.FcastManager +import com.lagradost.cloudstream3.utils.fcast.FcastSession +import com.lagradost.cloudstream3.utils.fcast.Opcode +import com.lagradost.cloudstream3.utils.fcast.PlayMessage import kotlinx.coroutines.* import java.io.File import java.util.concurrent.TimeUnit @@ -1519,6 +1523,13 @@ class ResultViewModel2 : ViewModel() { ) ) } + + if (FcastManager.currentDevices.isNotEmpty()) { + options.add( + txt(R.string.player_settings_play_in_fcast) to ACTION_FCAST + ) + } + options.add(txt(R.string.episode_action_play_in_app) to ACTION_PLAY_EPISODE_IN_PLAYER) for (app in apps) { @@ -1694,6 +1705,39 @@ class ResultViewModel2 : ViewModel() { } } + ACTION_FCAST -> { + val devices = FcastManager.currentDevices.toList() + postPopup( + txt(R.string.player_settings_select_cast_device), + devices.map { txt(it.name) }) { index -> + if (index == null) return@postPopup + val device = devices.getOrNull(index) + + acquireSingleLink( + click.data, + LoadType.Fcast, + txt(R.string.episode_action_cast_mirror) + ) { (result, index) -> + val host = device?.host ?: return@acquireSingleLink + val link = result.links.firstOrNull() ?: return@acquireSingleLink + + FcastSession(host).use { session -> + session.sendMessage( + Opcode.Play, + PlayMessage( + link.type.getMimeType(), + link.url, + headers = mapOf( + "referer" to link.referer, + "user-agent" to USER_AGENT + ) + link.headers + ) + ) + } + } + } + } + ACTION_PLAY_EPISODE_IN_BROWSER -> acquireSingleLink( click.data, LoadType.Browser, 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 0e4dc870..61cdd26a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -308,7 +308,18 @@ enum class ExtractorLinkType { /** No support at the moment */ TORRENT, /** No support at the moment */ - MAGNET, + MAGNET; + + // See https://www.iana.org/assignments/media-types/media-types.xhtml + fun getMimeType(): String { + return when (this) { + VIDEO -> "video/mp4" + M3U8 -> "application/x-mpegURL" + DASH -> "application/dash+xml" + TORRENT -> "application/x-bittorrent" + MAGNET -> "application/x-bittorrent" + } + } } private fun inferTypeFromUrl(url: String): ExtractorLinkType { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt new file mode 100644 index 00000000..9ff5cc08 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastManager.kt @@ -0,0 +1,135 @@ +package com.lagradost.cloudstream3.utils.fcast + +import android.content.Context +import android.net.nsd.NsdManager +import android.net.nsd.NsdManager.ResolveListener +import android.net.nsd.NsdServiceInfo +import android.os.Build +import android.util.Log +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe + +class FcastManager { + private var nsdManager: NsdManager? = null + + // Used for receiver + private val registrationListenerTcp = DefaultRegistrationListener() + private fun getDeviceName(): String { + return "${Build.MANUFACTURER}-${Build.MODEL}" + } + + /** + * Start the fcast service + * @param registerReceiver If true will register the app as a compatible fcast receiver for discovery in other app + */ + fun init(context: Context, registerReceiver: Boolean) = ioSafe { + nsdManager = context.getSystemService(Context.NSD_SERVICE) as NsdManager + val serviceType = "_fcast._tcp" + + if (registerReceiver) { + val serviceName = "$APP_PREFIX-${getDeviceName()}" + + val serviceInfo = NsdServiceInfo().apply { + this.serviceName = serviceName + this.serviceType = serviceType + this.port = TCP_PORT + } + + nsdManager?.registerService( + serviceInfo, + NsdManager.PROTOCOL_DNS_SD, + registrationListenerTcp + ) + } + + nsdManager?.discoverServices( + serviceType, + NsdManager.PROTOCOL_DNS_SD, + DefaultDiscoveryListener() + ) + } + + fun stop() { + nsdManager?.unregisterService(registrationListenerTcp) + } + + inner class DefaultDiscoveryListener : NsdManager.DiscoveryListener { + val tag = "DiscoveryListener" + override fun onStartDiscoveryFailed(serviceType: String?, errorCode: Int) { + Log.d(tag, "Discovery failed: $serviceType, error code: $errorCode") + } + + override fun onStopDiscoveryFailed(serviceType: String?, errorCode: Int) { + Log.d(tag, "Stop discovery failed: $serviceType, error code: $errorCode") + } + + override fun onDiscoveryStarted(serviceType: String?) { + Log.d(tag, "Discovery started: $serviceType") + } + + override fun onDiscoveryStopped(serviceType: String?) { + Log.d(tag, "Discovery stopped: $serviceType") + } + + override fun onServiceFound(serviceInfo: NsdServiceInfo?) { + if (serviceInfo == null) return + nsdManager?.resolveService(serviceInfo, object : ResolveListener { + override fun onResolveFailed(serviceInfo: NsdServiceInfo?, errorCode: Int) { + } + + override fun onServiceResolved(serviceInfo: NsdServiceInfo?) { + if (serviceInfo == null) return + + currentDevices.add(PublicDeviceInfo(serviceInfo)) + + Log.d( + tag, + "Service found: ${serviceInfo.serviceName}, Net: ${serviceInfo.host.hostAddress}" + ) + } + }) + } + + override fun onServiceLost(serviceInfo: NsdServiceInfo?) { + if (serviceInfo == null) return + + // May remove duplicates, but net and port is null here, preventing device specific identification + currentDevices.removeAll { + it.rawName == serviceInfo.serviceName + } + + Log.d(tag, "Service lost: ${serviceInfo.serviceName}") + } + } + + companion object { + const val APP_PREFIX = "CloudStream" + val currentDevices: MutableList = mutableListOf() + + class DefaultRegistrationListener : NsdManager.RegistrationListener { + val tag = "DiscoveryService" + override fun onServiceRegistered(serviceInfo: NsdServiceInfo) { + Log.d(tag, "Service registered: ${serviceInfo.serviceName}") + } + + override fun onRegistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { + Log.e(tag, "Service registration failed: errorCode=$errorCode") + } + + override fun onServiceUnregistered(serviceInfo: NsdServiceInfo) { + Log.d(tag, "Service unregistered: ${serviceInfo.serviceName}") + } + + override fun onUnregistrationFailed(serviceInfo: NsdServiceInfo, errorCode: Int) { + Log.e(tag, "Service unregistration failed: errorCode=$errorCode") + } + } + + const val TCP_PORT = 46899 + } +} + +class PublicDeviceInfo(serviceInfo: NsdServiceInfo) { + val rawName: String = serviceInfo.serviceName + val host: String? = serviceInfo.host.hostAddress + val name = rawName.replace("-", " ") + host?.let { " $it" } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt new file mode 100644 index 00000000..1f33bca4 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/FcastSession.kt @@ -0,0 +1,60 @@ +package com.lagradost.cloudstream3.utils.fcast + +import android.util.Log +import androidx.annotation.WorkerThread +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.Coroutines.ioSafe +import com.lagradost.safefile.closeQuietly +import java.io.DataOutputStream +import java.net.Socket +import kotlin.jvm.Throws + +class FcastSession(private val hostAddress: String): AutoCloseable { + val tag = "FcastSession" + + private var socket: Socket? = null + @Throws + @WorkerThread + fun open(): Socket { + val socket = Socket(hostAddress, FcastManager.TCP_PORT) + this.socket = socket + return socket + } + + override fun close() { + socket?.closeQuietly() + socket = null + } + + @Throws + private fun acquireSocket(): Socket { + return socket ?: open() + } + + fun ping() { + sendMessage(Opcode.Ping, null) + } + + fun sendMessage(opcode: Opcode, message: T) { + ioSafe { + val socket = acquireSocket() + val outputStream = DataOutputStream(socket.getOutputStream()) + + val json = message?.toJson() + val content = json?.toByteArray() ?: ByteArray(0) + + // Little endian starting from 1 + // https://gitlab.com/futo-org/fcast/-/wikis/Protocol-version-1 + val size = content.size + 1 + + val sizeArray = ByteArray(4) { num -> + (size shr 8 * num and 0xff).toByte() + } + + Log.d(tag, "Sending message with size: $size, opcode: $opcode") + outputStream.write(sizeArray) + outputStream.write(ByteArray(1) { opcode.value }) + outputStream.write(content) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt new file mode 100644 index 00000000..61c00d6e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/fcast/Packets.kt @@ -0,0 +1,62 @@ +package com.lagradost.cloudstream3.utils.fcast + +// See https://gitlab.com/futo-org/fcast/-/wikis/Protocol-version-1 +enum class Opcode(val value: Byte) { + None(0), + Play(1), + Pause(2), + Resume(3), + Stop(4), + Seek(5), + PlaybackUpdate(6), + VolumeUpdate(7), + SetVolume(8), + PlaybackError(9), + SetSpeed(10), + Version(11), + Ping(12), + Pong(13); +} + + +data class PlayMessage( + val container: String, + val url: String? = null, + val content: String? = null, + val time: Double? = null, + val speed: Double? = null, + val headers: Map? = null +) + +data class SeekMessage( + val time: Double +) + +data class PlaybackUpdateMessage( + val generationTime: Long, + val time: Double, + val duration: Double, + val state: Int, + val speed: Double +) + +data class VolumeUpdateMessage( + val generationTime: Long, + val volume: Double +) + +data class PlaybackErrorMessage( + val message: String +) + +data class SetSpeedMessage( + val speed: Double +) + +data class SetVolumeMessage( + val volume: Double +) + +data class VersionMessage( + val version: Long +) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9bd2426c..a8108623 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -357,6 +357,7 @@ Download error, check storage permissions Chromecast episode Chromecast mirror + Cast mirror Play in app Play in %s Play in browser @@ -634,7 +635,9 @@ VLC MPV Web Video Cast + Fcast Web browser + Select cast device App not found All Languages Skip %s From af828de8d5264e7d2c3a6d6954b0a2a228ca2264 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 18 May 2024 14:41:37 +0300 Subject: [PATCH 10/24] feat(TV UI: Fix online subtitles dialog focus (#1085) --- app/src/main/res/layout/dialog_online_subtitles.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/layout/dialog_online_subtitles.xml b/app/src/main/res/layout/dialog_online_subtitles.xml index 7803e261..d480bd34 100644 --- a/app/src/main/res/layout/dialog_online_subtitles.xml +++ b/app/src/main/res/layout/dialog_online_subtitles.xml @@ -40,7 +40,7 @@ android:layout_width="match_parent" android:layout_height="30dp" android:layout_gravity="center_vertical" - android:layout_marginEnd="30dp"> + android:layout_marginEnd="40dp"> @@ -106,7 +107,7 @@ android:layout_margin="10dp" android:background="?selectableItemBackgroundBorderless" android:contentDescription="@string/change_providers_img_des" - android:nextFocusLeft="@id/main_search" + android:nextFocusLeft="@id/year_btt" android:nextFocusRight="@id/main_search" android:nextFocusUp="@id/nav_rail_view" android:nextFocusDown="@id/search_autofit_results" From 4d5cd288abd07c74fc88900c58e119c27f1b7867 Mon Sep 17 00:00:00 2001 From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com> Date: Sat, 18 May 2024 11:47:12 +0000 Subject: [PATCH 11/24] Ported more files for multiplatform (#1056) --- .../lagradost/cloudstream3/CommonActivity.kt | 1 + .../com/lagradost/cloudstream3/MainAPI.kt | 114 ++++++++-- .../lagradost/cloudstream3/MainActivity.kt | 197 ++++++++---------- .../ui/result/ResultViewModel2.kt | 10 +- .../cloudstream3/utils/Coroutines.android.kt | 11 + .../lagradost/cloudstream3/MainActivity.kt | 35 ++++ .../com/lagradost/cloudstream3/MainApi.kt | 3 + .../lagradost/cloudstream3/ParCollections.kt | 0 .../cloudstream3/utils/Coroutines.kt | 8 +- .../lagradost/cloudstream3/utils/JsHunter.kt | 0 .../cloudstream3/utils/JsUnpacker.kt | 0 .../cloudstream3/utils/Coroutines.jvm.kt | 5 + 12 files changed, 243 insertions(+), 141 deletions(-) create mode 100644 library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.android.kt create mode 100644 library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainActivity.kt rename {app/src/main/java => library/src/commonMain/kotlin}/com/lagradost/cloudstream3/ParCollections.kt (100%) rename {app/src/main/java => library/src/commonMain/kotlin}/com/lagradost/cloudstream3/utils/Coroutines.kt (90%) rename {app/src/main/java => library/src/commonMain/kotlin}/com/lagradost/cloudstream3/utils/JsHunter.kt (100%) rename {app/src/main/java => library/src/commonMain/kotlin}/com/lagradost/cloudstream3/utils/JsUnpacker.kt (100%) create mode 100644 library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvm.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt index 4dc78dc7..82e985db 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/CommonActivity.kt @@ -29,6 +29,7 @@ import com.google.android.material.chip.ChipGroup import com.google.android.material.navigationrail.NavigationRailView import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.MainActivity.Companion.resumeApps import com.lagradost.cloudstream3.databinding.ToastBinding import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.ui.player.PlayerEventType diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 699159b5..07a82583 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -31,19 +31,16 @@ import java.text.SimpleDateFormat import java.util.* import kotlin.math.absoluteValue -const val USER_AGENT = - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" - -//val baseHeader = mapOf("User-Agent" to USER_AGENT) -val mapper = JsonMapper.builder().addModule(kotlinModule()) - .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!! - /** * Defines the constant for the all languages preference, if this is set then it is * the equivalent of all languages being set **/ const val AllLanguagesName = "universal" +//val baseHeader = mapOf("User-Agent" to USER_AGENT) +val mapper = JsonMapper.builder().addModule(kotlinModule()) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()!! + object APIHolder { val unixTime: Long get() = System.currentTimeMillis() / 1000L @@ -121,7 +118,8 @@ object APIHolder { fun LoadResponse.getId(): Int { // this fixes an issue with outdated api as getLoadResponseIdFromUrl might be fucked - return (if (this is ResultViewModel2.LoadResponseFromSearch) this.id else null) ?: getLoadResponseIdFromUrl(url, apiName) + return (if (this is ResultViewModel2.LoadResponseFromSearch) this.id else null) + ?: getLoadResponseIdFromUrl(url, apiName) } /** @@ -222,10 +220,15 @@ object APIHolder { } ?: false val matchingTypes = types?.any { it.name.equals(media.format, true) } == true - if(lessAccurate) matchingTitles || matchingTypes && matchingYears else matchingTitles && matchingTypes && matchingYears + if (lessAccurate) matchingTitles || matchingTypes && matchingYears else matchingTitles && matchingTypes && matchingYears } ?: return null - Tracker(res.idMal, res.id.toString(), res.coverImage?.extraLarge ?: res.coverImage?.large, res.bannerImage) + Tracker( + res.idMal, + res.id.toString(), + res.coverImage?.extraLarge ?: res.coverImage?.large, + res.bannerImage + ) } catch (t: Throwable) { logError(t) null @@ -866,6 +869,7 @@ enum class TvType(value: Int?) { Others(12), Music(13), AudioBook(14), + /** Wont load the built in player, make your own interaction */ CustomMedia(15), } @@ -1253,13 +1257,15 @@ interface LoadResponse { fun LoadResponse.getImdbId(): String? { return normalSafeApiCall { - SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Imdb) + SimklApi.readIdFromString(this.syncData[simklIdPrefix]) + ?.get(SimklApi.Companion.SyncServices.Imdb) } } fun LoadResponse.getTMDbId(): String? { return normalSafeApiCall { - SimklApi.readIdFromString(this.syncData[simklIdPrefix])?.get(SimklApi.Companion.SyncServices.Tmdb) + SimklApi.readIdFromString(this.syncData[simklIdPrefix]) + ?.get(SimklApi.Companion.SyncServices.Tmdb) } } @@ -1556,8 +1562,26 @@ data class TorrentLoadResponse( posterHeaders: Map? = null, backgroundPosterUrl: String? = null, ) : this( - name, url, apiName, magnet, torrent, plot, type, posterUrl, year, rating, tags, duration, trailers, - recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null + name, + url, + apiName, + magnet, + torrent, + plot, + type, + posterUrl, + year, + rating, + tags, + duration, + trailers, + recommendations, + actors, + comingSoon, + syncData, + posterHeaders, + backgroundPosterUrl, + null ) } @@ -1609,7 +1633,8 @@ data class AnimeLoadResponse( return this.episodes.maxOf { (_, episodes) -> episodes.count { episodeData -> // Prioritize display season as actual season may be something random to fit multiple seasons into one. - val episodeSeason = displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE + val episodeSeason = + displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE // Count all episodes from season 1 to below the current season. episodeSeason in 1..? = null, backgroundPosterUrl: String? = null, ) : this( - engName, japName, name, url, apiName, type, posterUrl, year, episodes, showStatus, plot, tags, - synonyms, rating, duration, trailers, recommendations, actors, comingSoon, syncData, posterHeaders, - nextAiring, seasonNames, backgroundPosterUrl, null + engName, + japName, + name, + url, + apiName, + type, + posterUrl, + year, + episodes, + showStatus, + plot, + tags, + synonyms, + rating, + duration, + trailers, + recommendations, + actors, + comingSoon, + syncData, + posterHeaders, + nextAiring, + seasonNames, + backgroundPosterUrl, + null ) } @@ -1780,7 +1827,7 @@ data class MovieLoadResponse( backgroundPosterUrl: String? = null, ) : this( name, url, apiName, type, dataUrl, posterUrl, year, plot, rating, tags, duration, trailers, - recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl,null + recommendations, actors, comingSoon, syncData, posterHeaders, backgroundPosterUrl, null ) } @@ -1923,7 +1970,8 @@ data class TvSeriesLoadResponse( return episodes.count { episodeData -> // Prioritize display season as actual season may be something random to fit multiple seasons into one. - val episodeSeason = displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE + val episodeSeason = + displayMap[episodeData.season] ?: episodeData.season ?: Int.MIN_VALUE // Count all episodes from season 1 to below the current season. episodeSeason in 1..? = null, backgroundPosterUrl: String? = null, ) : this( - name, url, apiName, type, episodes, posterUrl, year, plot, showStatus, rating, tags, duration, - trailers, recommendations, actors, comingSoon, syncData, posterHeaders, nextAiring, seasonNames, - backgroundPosterUrl, null + name, + url, + apiName, + type, + episodes, + posterUrl, + year, + plot, + showStatus, + rating, + tags, + duration, + trailers, + recommendations, + actors, + comingSoon, + syncData, + posterHeaders, + nextAiring, + seasonNames, + backgroundPosterUrl, + null ) } @@ -2022,6 +2089,7 @@ data class AniSearch( @JsonProperty("extraLarge") var extraLarge: String? = null, @JsonProperty("large") var large: String? = null, ) + data class Title( @JsonProperty("romaji") var romaji: String? = null, @JsonProperty("english") var english: String? = null, diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 56322b73..1ff0575b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -174,7 +174,6 @@ import java.net.URLDecoder import java.nio.charset.Charset import kotlin.math.abs import kotlin.math.absoluteValue -import kotlin.reflect.KClass import kotlin.system.exitProcess //https://github.com/videolan/vlc-android/blob/3706c4be2da6800b3d26344fc04fab03ffa4b860/application/vlc-android/src/org/videolan/vlc/gui/video/VideoPlayerActivity.kt#L1898 @@ -187,117 +186,93 @@ import kotlin.system.exitProcess //https://github.com/jellyfin/jellyfin-android/blob/6cbf0edf84a3da82347c8d59b5d5590749da81a9/app/src/main/java/org/jellyfin/mobile/bridge/ExternalPlayer.kt#L225 -const val VLC_PACKAGE = "org.videolan.vlc" -const val MPV_PACKAGE = "is.xyz.mpv" -const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo" - -val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity") -val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity") - -//TODO REFACTOR AF -open class ResultResume( - val packageString: String, - val action: String = Intent.ACTION_VIEW, - val position: String? = null, - val duration: String? = null, - var launcher: ActivityResultLauncher? = null, -) { - val defaultTime = -1L - - val lastId get() = "${packageString}_last_open_id" - suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) { - val intent = Intent(action) - - if (id != null) - setKey(lastId, id) - else - removeKey(lastId) - - intent.setPackage(packageString) - callback.invoke(intent) - launcher?.launch(intent) - } - - open fun getPosition(intent: Intent?): Long { - return defaultTime - } - - open fun getDuration(intent: Intent?): Long { - return defaultTime - } -} - -val VLC = object : ResultResume( - VLC_PACKAGE, - // Android 13 intent restrictions fucks up specifically launching the VLC player - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { - "org.videolan.vlc.player.result" - } else { - Intent.ACTION_VIEW - }, - "extra_position", - "extra_duration", -) { - override fun getPosition(intent: Intent?): Long { - return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime - } - - override fun getDuration(intent: Intent?): Long { - return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime - } -} - -val MPV = object : ResultResume( - MPV_PACKAGE, - //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive: - position = "position", - duration = "duration", -) { - override fun getPosition(intent: Intent?): Long { - return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong() ?: defaultTime - } - - override fun getDuration(intent: Intent?): Long { - return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong() ?: defaultTime - } -} - -val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE) - -val resumeApps = arrayOf( - VLC, MPV, WEB_VIDEO -) - -// Short name for requests client to make it nicer to use - -var app = Requests(responseParser = object : ResponseParser { - val mapper: ObjectMapper = jacksonObjectMapper().configure( - DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, - false - ) - - override fun parse(text: String, kClass: KClass): T { - return mapper.readValue(text, kClass.java) - } - - override fun parseSafe(text: String, kClass: KClass): T? { - return try { - mapper.readValue(text, kClass.java) - } catch (e: Exception) { - null - } - } - - override fun writeValueAsString(obj: Any): String { - return mapper.writeValueAsString(obj) - } -}).apply { - defaultHeaders = mapOf("user-agent" to USER_AGENT) -} - class MainActivity : AppCompatActivity(), ColorPickerDialogListener, BiometricAuthenticator.BiometricAuthCallback { companion object { + const val VLC_PACKAGE = "org.videolan.vlc" + const val MPV_PACKAGE = "is.xyz.mpv" + const val WEB_VIDEO_CAST_PACKAGE = "com.instantbits.cast.webvideo" + + val VLC_COMPONENT = ComponentName(VLC_PACKAGE, "$VLC_PACKAGE.gui.video.VideoPlayerActivity") + val MPV_COMPONENT = ComponentName(MPV_PACKAGE, "$MPV_PACKAGE.MPVActivity") + + //TODO REFACTOR AF + open class ResultResume( + val packageString: String, + val action: String = Intent.ACTION_VIEW, + val position: String? = null, + val duration: String? = null, + var launcher: ActivityResultLauncher? = null, + ) { + val defaultTime = -1L + + val lastId get() = "${packageString}_last_open_id" + suspend fun launch(id: Int?, callback: suspend Intent.() -> Unit) { + val intent = Intent(action) + + if (id != null) + setKey(lastId, id) + else + removeKey(lastId) + + intent.setPackage(packageString) + callback.invoke(intent) + launcher?.launch(intent) + } + + open fun getPosition(intent: Intent?): Long { + return defaultTime + } + + open fun getDuration(intent: Intent?): Long { + return defaultTime + } + } + + val VLC = object : ResultResume( + VLC_PACKAGE, + // Android 13 intent restrictions fucks up specifically launching the VLC player + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) { + "org.videolan.vlc.player.result" + } else { + Intent.ACTION_VIEW + }, + "extra_position", + "extra_duration", + ) { + override fun getPosition(intent: Intent?): Long { + return intent?.getLongExtra(this.position, defaultTime) ?: defaultTime + } + + override fun getDuration(intent: Intent?): Long { + return intent?.getLongExtra(this.duration, defaultTime) ?: defaultTime + } + } + + val MPV = object : ResultResume( + MPV_PACKAGE, + //"is.xyz.mpv.MPVActivity.result", // resume not working :pensive: + position = "position", + duration = "duration", + ) { + override fun getPosition(intent: Intent?): Long { + return intent?.getIntExtra(this.position, defaultTime.toInt())?.toLong() + ?: defaultTime + } + + override fun getDuration(intent: Intent?): Long { + return intent?.getIntExtra(this.duration, defaultTime.toInt())?.toLong() + ?: defaultTime + } + } + + val WEB_VIDEO = ResultResume(WEB_VIDEO_CAST_PACKAGE) + + val resumeApps = arrayOf( + VLC, MPV, WEB_VIDEO + ) + + const val TAG = "MAINACT" const val ANIMATED_OUTLINE: Boolean = false var lastError: String? = null @@ -1403,7 +1378,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, } } - observe(viewModel.watchStatus,::setWatchStatus) + observe(viewModel.watchStatus, ::setWatchStatus) observe(syncViewModel.userData, ::setUserData) observeNullable(viewModel.subscribeStatus, ::setSubscribeStatus) @@ -1831,7 +1806,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, } override fun onAuthenticationError() { - finish() + finish() } private var backPressedCallback: OnBackPressedCallback? = null 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 a32942f6..0af01ca8 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 @@ -29,6 +29,14 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie +import com.lagradost.cloudstream3.MainActivity.Companion.MPV +import com.lagradost.cloudstream3.MainActivity.Companion.MPV_COMPONENT +import com.lagradost.cloudstream3.MainActivity.Companion.MPV_PACKAGE +import com.lagradost.cloudstream3.MainActivity.Companion.VLC +import com.lagradost.cloudstream3.MainActivity.Companion.VLC_COMPONENT +import com.lagradost.cloudstream3.MainActivity.Companion.VLC_PACKAGE +import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO +import com.lagradost.cloudstream3.MainActivity.Companion.WEB_VIDEO_CAST_PACKAGE import com.lagradost.cloudstream3.metaproviders.SyncRedirector import com.lagradost.cloudstream3.mvvm.* import com.lagradost.cloudstream3.syncproviders.AccountManager @@ -1354,7 +1362,7 @@ class ResultViewModel2 : ViewModel() { private fun launchActivity( activity: Activity?, - resumeApp: ResultResume, + resumeApp: MainActivity.Companion.ResultResume, id: Int? = null, work: suspend (Intent.(Activity) -> Unit) ): Job? { diff --git a/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.android.kt b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.android.kt new file mode 100644 index 00000000..48a709eb --- /dev/null +++ b/library/src/androidMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.android.kt @@ -0,0 +1,11 @@ +package com.lagradost.cloudstream3.utils + +import android.os.Handler +import android.os.Looper + +actual fun runOnMainThreadNative(work: () -> Unit) { + val mainHandler = Handler(Looper.getMainLooper()) + mainHandler.post { + work() + } +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainActivity.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainActivity.kt new file mode 100644 index 00000000..6502cc83 --- /dev/null +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainActivity.kt @@ -0,0 +1,35 @@ +package com.lagradost.cloudstream3 + +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.lagradost.nicehttp.Requests +import com.lagradost.nicehttp.ResponseParser +import kotlin.reflect.KClass + +// Short name for requests client to make it nicer to use + +var app = Requests(responseParser = object : ResponseParser { + val mapper: ObjectMapper = jacksonObjectMapper().configure( + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + false + ) + + override fun parse(text: String, kClass: KClass): T { + return mapper.readValue(text, kClass.java) + } + + override fun parseSafe(text: String, kClass: KClass): T? { + return try { + mapper.readValue(text, kClass.java) + } catch (e: Exception) { + null + } + } + + override fun writeValueAsString(obj: Any): String { + return mapper.writeValueAsString(obj) + } +}).apply { + defaultHeaders = mapOf("user-agent" to USER_AGENT) +} \ No newline at end of file diff --git a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainApi.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainApi.kt index 87ee4815..160ff098 100644 --- a/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainApi.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/MainApi.kt @@ -1,3 +1,6 @@ package com.lagradost.cloudstream3 +const val USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" + class ErrorLoadingException(message: String? = null) : Exception(message) \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/ParCollections.kt similarity index 100% rename from app/src/main/java/com/lagradost/cloudstream3/ParCollections.kt rename to library/src/commonMain/kotlin/com/lagradost/cloudstream3/ParCollections.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt similarity index 90% rename from app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt rename to library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt index c3b244c2..f87ddc6a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/Coroutines.kt +++ b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.kt @@ -1,12 +1,11 @@ package com.lagradost.cloudstream3.utils -import android.os.Handler -import android.os.Looper import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.logError import kotlinx.coroutines.* import java.util.Collections.synchronizedList +expect fun runOnMainThreadNative(work: (() -> Unit)) object Coroutines { fun T.main(work: suspend ((T) -> Unit)): Job { val value = this @@ -50,10 +49,7 @@ object Coroutines { } fun runOnMainThread(work: (() -> Unit)) { - val mainHandler = Handler(Looper.getMainLooper()) - mainHandler.post { - work() - } + runOnMainThreadNative(work) } /** diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/JsHunter.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsHunter.kt similarity index 100% rename from app/src/main/java/com/lagradost/cloudstream3/utils/JsHunter.kt rename to library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsHunter.kt diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/JsUnpacker.kt b/library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsUnpacker.kt similarity index 100% rename from app/src/main/java/com/lagradost/cloudstream3/utils/JsUnpacker.kt rename to library/src/commonMain/kotlin/com/lagradost/cloudstream3/utils/JsUnpacker.kt diff --git a/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvm.kt b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvm.kt new file mode 100644 index 00000000..0a9667cb --- /dev/null +++ b/library/src/jvmMain/kotlin/com/lagradost/cloudstream3/utils/Coroutines.jvm.kt @@ -0,0 +1,5 @@ +package com.lagradost.cloudstream3.utils + +actual fun runOnMainThreadNative(work: () -> Unit) { + work.invoke() +} \ No newline at end of file From 469a71236b6e78018f3f72c83f0b051bdab189c3 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 18 May 2024 19:15:23 +0300 Subject: [PATCH 12/24] SubDL subtitles provider (#1082) --- .../subtitles/AbstractSubtitleEntities.kt | 6 +- .../syncproviders/AccountManager.kt | 5 +- .../providers/IndexSubtitleApi.kt | 2 +- .../providers/OpenSubtitlesApi.kt | 2 +- .../syncproviders/providers/Subdl.kt | 102 ++++++++++++++++++ .../ui/player/FullScreenPlayer.kt | 3 +- .../cloudstream3/ui/player/GeneratorPlayer.kt | 18 +++- .../ui/player/PlayerGeneratorViewModel.kt | 4 + .../ui/result/ResultTrailerPlayer.kt | 3 +- .../ui/result/ResultViewModel2.kt | 3 +- 10 files changed, 136 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt 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 f6424c4c..ed4ccb74 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/subtitles/AbstractSubtitleEntities.kt @@ -1,5 +1,6 @@ package com.lagradost.cloudstream3.subtitles +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.TvType class AbstractSubtitleEntities { @@ -19,8 +20,11 @@ class AbstractSubtitleEntities { data class SubtitleSearch( var query: String = "", - var imdb: Long? = null, var lang: String? = null, + var imdbId: String? = null, + var tmdbId: Int? = null, + var malId: Int? = null, + var aniListId: Int? = null, var epNumber: Int? = null, var seasonNumber: Int? = null, var year: Int? = null 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 bae8a5df..55418890 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -3,7 +3,6 @@ package com.lagradost.cloudstream3.syncproviders import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys import com.lagradost.cloudstream3.AcraApplication.Companion.setKey -import com.lagradost.cloudstream3.syncproviders.providers.SubScene import com.lagradost.cloudstream3.syncproviders.providers.* import java.util.concurrent.TimeUnit @@ -16,6 +15,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() val subScene = SubScene() + val subDl = SubDL() val localListApi = LocalList() // used to login via app intent @@ -44,7 +44,8 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { openSubtitlesApi, indexSubtitlesApi, // they got anti scraping measures in place :( addic7ed, - subScene + subScene, + subDl ) const val appString = "cloudstreamapp" 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 1adecce9..5ca3f3d5 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 @@ -98,7 +98,7 @@ class IndexSubtitleApi : AbstractSubApi { } override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List { - val imdbId = query.imdb ?: 0 + val imdbId = query.imdbId?.replace("tt", "")?.toLong() ?: 0 val lang = query.lang val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString()) val queryText = query.query 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 4030649d..7d0514d1 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 @@ -185,7 +185,7 @@ class OpenSubtitlesApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi throwIfCantDoRequest() val fixedLang = fixLanguage(query.lang) - val imdbId = query.imdb ?: 0 + val imdbId = query.imdbId?.replace("tt", "")?.toInt() ?: 0 val queryText = query.query val epNum = query.epNumber ?: 0 val seasonNum = query.seasonNumber ?: 0 diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt new file mode 100644 index 00000000..d25d3f22 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt @@ -0,0 +1,102 @@ +package com.lagradost.cloudstream3.syncproviders.providers + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.subtitles.AbstractSubProvider +import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities +import com.lagradost.cloudstream3.subtitles.SubtitleResource + +class SubDL : AbstractSubProvider { + //API Documentation: https://subdl.com/api-doc + val mainUrl = "https://subdl.com/" + val name = "SubDL" + override val idPrefix = "subdl" + companion object { + const val APIKEY = "zRJl5QA-8jNA2i0pE8cxANbEukANp7IM" + const val APIENDPOINT = "https://api.subdl.com/api/v1/subtitles" + const val DOWNLOADENDPOINT = "https://dl.subdl.com" + } + + override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List? { + + val queryText = query.query + val epNum = query.epNumber ?: 0 + val seasonNum = query.seasonNumber ?: 0 + val yearNum = query.year ?: 0 + + val idQuery = when { + query.imdbId != null -> "&imdb_id=${query.imdbId}" + query.tmdbId != null -> "&tmdb_id=${query.tmdbId}" + else -> null + } + + val epQuery = if (epNum > 0) "&episode_number=$epNum" else "" + val seasonQuery = if (seasonNum > 0) "&season_number=$seasonNum" else "" + val yearQuery = if (yearNum > 0) "&year=$yearNum" else "" + + val searchQueryUrl = when (idQuery) { + //Use imdb/tmdb id to search if its valid + null -> "$APIENDPOINT?api_key=$APIKEY&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery" + else -> "$APIENDPOINT?api_key=$APIKEY$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery" + } + + val req = app.get( + url = searchQueryUrl, + headers = mapOf( + "Accept" to "application/json" + ) + ) + + return req.parsedSafe()?.subtitles?.map { subtitle -> + val name = subtitle.releaseName + val lang = subtitle.lang.replaceFirstChar { it.uppercase() } + val resEpNum = subtitle.episode ?: query.epNumber + val resSeasonNum = subtitle.season ?: query.seasonNumber + val type = if ((resSeasonNum ?: 0) > 0) TvType.TvSeries else TvType.Movie + + AbstractSubtitleEntities.SubtitleEntity( + idPrefix = this.idPrefix, + name = name, + lang = lang, + data = "${DOWNLOADENDPOINT}${subtitle.url}", + type = type, + source = this.name, + epNumber = resEpNum, + seasonNumber = resSeasonNum, + ) + } + } + + override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) { + this.addZipUrl(data.data) { name, _ -> + name + } + } + + data class ApiResponse( + @JsonProperty("status") val status: Boolean? = null, + @JsonProperty("results") val results: List? = null, + @JsonProperty("subtitles") val subtitles: List? = null, + ) + + data class Result( + @JsonProperty("sd_id") val sdId: Int? = null, + @JsonProperty("type") val type: String? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("imdb_id") val imdbId: String? = null, + @JsonProperty("tmdb_id") val tmdbId: Long? = null, + @JsonProperty("first_air_date") val firstAirDate: String? = null, + @JsonProperty("year") val year: Int? = null, + ) + + data class Subtitle( + @JsonProperty("release_name") val releaseName: String, + @JsonProperty("name") val name: String, + @JsonProperty("lang") val lang: String, + @JsonProperty("author") val author: String? = null, + @JsonProperty("url") val url: String? = null, + @JsonProperty("season") val season: Int? = null, + @JsonProperty("episode") val episode: Int? = null, + ) +} \ No newline at end of file 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 c357ce9c..aa25157b 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 @@ -32,6 +32,7 @@ import com.lagradost.cloudstream3.CommonActivity.keyEventListener import com.lagradost.cloudstream3.CommonActivity.playerEventListener import com.lagradost.cloudstream3.CommonActivity.screenHeight import com.lagradost.cloudstream3.CommonActivity.screenWidth +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding import com.lagradost.cloudstream3.databinding.SubtitleOffsetBinding @@ -177,7 +178,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { open fun openOnlineSubPicker( context: Context, - imdbId: Long?, + loadResponse: LoadResponse?, dismissCallback: (() -> Unit) ) { throw NotImplementedError() 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 7ff56886..c77f9404 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 @@ -25,6 +25,10 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull import com.lagradost.cloudstream3.AcraApplication.Companion.setKey import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId +import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId +import com.lagradost.cloudstream3.LoadResponse.Companion.getTMDbId import com.lagradost.cloudstream3.databinding.DialogOnlineSubtitlesBinding import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding import com.lagradost.cloudstream3.databinding.PlayerSelectSourceAndSubsBinding @@ -39,7 +43,6 @@ import com.lagradost.cloudstream3.ui.player.PlayerSubtitleHelper.Companion.toSub import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper import com.lagradost.cloudstream3.ui.player.source_priority.QualityProfileDialog import com.lagradost.cloudstream3.ui.result.* -import com.lagradost.cloudstream3.ui.settings.Globals import com.lagradost.cloudstream3.ui.settings.Globals.EMULATOR import com.lagradost.cloudstream3.ui.settings.Globals.TV import com.lagradost.cloudstream3.ui.settings.Globals.isLayout @@ -258,6 +261,7 @@ class GeneratorPlayer : FullScreenPlayer() { var episode: Int? = null, var season: Int? = null, var name: String? = null, + var imdbId: String? = null, ) private fun getMetaData(): TempMetaData { @@ -284,7 +288,7 @@ class GeneratorPlayer : FullScreenPlayer() { } override fun openOnlineSubPicker( - context: Context, imdbId: Long?, dismissCallback: (() -> Unit) + context: Context, loadResponse: LoadResponse?, dismissCallback: (() -> Unit) ) { val providers = subsProviders val isSingleProvider = subsProviders.size == 1 @@ -377,6 +381,7 @@ class GeneratorPlayer : FullScreenPlayer() { } val currentTempMeta = getMetaData() + // bruh idk why it is not correct val color = ColorStateList.valueOf(context.colorFromAttribute(R.attr.colorAccent)) binding.searchLoadingBar.progressTintList = color @@ -424,7 +429,10 @@ class GeneratorPlayer : FullScreenPlayer() { val search = AbstractSubtitleEntities.SubtitleSearch( query = query ?: return@ioSafe, - imdb = imdbId, + imdbId = loadResponse?.getImdbId(), + tmdbId = loadResponse?.getTMDbId()?.toInt(), + malId = loadResponse?.getMalId()?.toInt(), + aniListId = loadResponse?.getAniListId()?.toInt(), epNumber = currentTempMeta.episode, seasonNumber = currentTempMeta.season, lang = currentLanguageTwoLetters.ifBlank { null }, @@ -633,6 +641,8 @@ class GeneratorPlayer : FullScreenPlayer() { } if (subsProvidersIsActive) { + val currentLoadResponse = viewModel.getLoadResponse() + val loadFromOpenSubsFooter: TextView = layoutInflater.inflate( R.layout.sort_bottom_footer_add_choice, null ) as TextView @@ -643,7 +653,7 @@ class GeneratorPlayer : FullScreenPlayer() { loadFromOpenSubsFooter.setOnClickListener { shouldDismiss = false sourceDialog.dismissSafe(activity) - openOnlineSubPicker(it.context, null) { + openOnlineSubPicker(it.context, currentLoadResponse) { dismiss() } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt index 0d98f205..ee44567f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/PlayerGeneratorViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.mvvm.Resource import com.lagradost.cloudstream3.mvvm.launchSafe import com.lagradost.cloudstream3.mvvm.logError @@ -111,6 +112,9 @@ class PlayerGeneratorViewModel : ViewModel() { } } } + fun getLoadResponse(): LoadResponse? { + return normalSafeApiCall { (generator as? RepoLinkGenerator?)?.page } + } fun getMeta(): Any? { return normalSafeApiCall { generator?.getCurrent() } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt index ef3db0b4..135dc530 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultTrailerPlayer.kt @@ -12,6 +12,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import com.lagradost.cloudstream3.CommonActivity.screenHeight import com.lagradost.cloudstream3.CommonActivity.screenWidth +import com.lagradost.cloudstream3.LoadResponse import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.ui.player.CSPlayerEvent import com.lagradost.cloudstream3.ui.player.PlayerEventSource @@ -110,7 +111,7 @@ open class ResultTrailerPlayer : ResultFragmentPhone() { override fun openOnlineSubPicker( context: Context, - imdbId: Long?, + loadResponse: LoadResponse?, dismissCallback: () -> Unit ) { } 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 0af01ca8..e1a52074 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 @@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.CommonActivity.getCastSession import com.lagradost.cloudstream3.CommonActivity.showToast import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.LoadResponse.Companion.getAniListId +import com.lagradost.cloudstream3.LoadResponse.Companion.getImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.getMalId import com.lagradost.cloudstream3.LoadResponse.Companion.isMovie import com.lagradost.cloudstream3.MainActivity.Companion.MPV @@ -2417,7 +2418,7 @@ class ResultViewModel2 : ViewModel() { null, loadResponse.type, mainId, - null + null, ) ) } From db2bf5e7be3f952e440e02989a0c0878c4bc4b15 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 19 May 2024 18:43:46 +0800 Subject: [PATCH 13/24] Remove subscene (#1096) subscene.com just shows a "Subscene is closed" message now. --- .../syncproviders/AccountManager.kt | 2 - .../syncproviders/providers/SubScene.kt | 118 ------------------ 2 files changed, 120 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SubScene.kt 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 55418890..e96499f0 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -14,7 +14,6 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val simklApi = SimklApi(0) val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() - val subScene = SubScene() val subDl = SubDL() val localListApi = LocalList() @@ -44,7 +43,6 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { openSubtitlesApi, indexSubtitlesApi, // they got anti scraping measures in place :( addic7ed, - subScene, subDl ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SubScene.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SubScene.kt deleted file mode 100644 index fbe05026..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/SubScene.kt +++ /dev/null @@ -1,118 +0,0 @@ -package com.lagradost.cloudstream3.syncproviders.providers - -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.mvvm.debugPrint -import com.lagradost.cloudstream3.subtitles.AbstractSubProvider -import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities -import com.lagradost.cloudstream3.subtitles.SubtitleResource -import com.lagradost.cloudstream3.syncproviders.providers.IndexSubtitleApi.Companion.getOrdinal -import com.lagradost.cloudstream3.utils.SubtitleHelper - -class SubScene : AbstractSubProvider { - val mainUrl = "https://subscene.com" - val name = "Subscene" - override val idPrefix = "subscene" - - override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List? { - val seasonName = - query.seasonNumber?.let { number -> - // Need to translate "7" to "Seventh Season" - getOrdinal(number)?.let { words -> " - $words Season" } - } ?: "" - - val fullQuery = query.query + seasonName - - val doc = app.post( - "$mainUrl/subtitles/searchbytitle", - data = mapOf("query" to fullQuery, "l" to "") - ).document - - return doc.select("div.title a").map { element -> - val href = "$mainUrl${element.attr("href")}" - val title = element.text() - - AbstractSubtitleEntities.SubtitleEntity( - idPrefix = idPrefix, - name = title, - source = name, - data = href, - lang = query.lang ?: "en", - epNumber = query.epNumber - ) - }.distinctBy { it.data } - } - - override suspend fun SubtitleResource.getResources(data: AbstractSubtitleEntities.SubtitleEntity) { - val resultDoc = app.get(data.data).document - val queryLanguage = SubtitleHelper.fromTwoLettersToLanguage(data.lang) ?: "English" - - val results = resultDoc.select("table tbody tr").mapNotNull { element -> - val anchor = element.select("a") - val href = anchor.attr("href") ?: return@mapNotNull null - val fixedHref = "$mainUrl${href}" - val spans = anchor.select("span") - val language = spans.firstOrNull()?.text() - val title = spans.getOrNull(1)?.text() - val isPositive = anchor.select("span.positive-icon").isNotEmpty() - - TableElement(title, language, fixedHref, isPositive) - }.sortedBy { - it.getScore(queryLanguage, data.epNumber) - } - - debugPrint { "$name found subtitles: ${results.takeLast(3)}" } - // Last = highest score - val selectedResult = results.lastOrNull() ?: return - - val subtitleDocument = app.get(selectedResult.href).document - val subtitleDownloadUrl = - "$mainUrl${subtitleDocument.select("div.download a").attr("href")}" - - this.addZipUrl(subtitleDownloadUrl) { name, _ -> - name - } - } - - /** - * Class to manage the various different subtitle results and rank them. - */ - data class TableElement( - val title: String?, - val language: String?, - val href: String, - val isPositive: Boolean - ) { - private fun matchesLanguage(other: String): Boolean { - return language != null && (language.contains(other, ignoreCase = true) || - other.contains(language, ignoreCase = true)) - } - - /** - * Scores in this order: - * Preferred Language > Episode number > Positive rating > English Language - */ - fun getScore(queryLanguage: String, episodeNum: Int?): Int { - var score = 0 - if (this.matchesLanguage(queryLanguage)) { - score += 8 - } - // Matches Episode 7 using "E07" with any number of leading zeroes - if (episodeNum != null && title != null && title.contains( - Regex( - """E0*${episodeNum}""", - RegexOption.IGNORE_CASE - ) - ) - ) { - score += 4 - } - if (isPositive) { - score += 2 - } - if (this.matchesLanguage("English")) { - score += 1 - } - return score - } - } -} \ No newline at end of file From e697bf75544d0777e21f67c5af3bef8392bbd35b Mon Sep 17 00:00:00 2001 From: KingLucius Date: Tue, 21 May 2024 23:06:28 +0300 Subject: [PATCH 14/24] Next Airing episode support in Trakt meta provider (#1072) --- .../cloudstream3/metaproviders/TraktProvider.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt index 37c6be1b..07c9f316 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TraktProvider.kt @@ -4,6 +4,7 @@ import android.net.Uri import com.lagradost.cloudstream3.* import com.fasterxml.jackson.annotation.JsonAlias import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.APIHolder.unixTimeMS import com.lagradost.cloudstream3.LoadResponse.Companion.addImdbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTMDbId import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer @@ -166,6 +167,7 @@ open class TraktProvider : MainAPI() { val episodes = mutableListOf() val seasons = parseJson>(resSeasons) val seasonsNames = mutableListOf() + var nextAir: NextAiring? = null seasons.forEach { season -> @@ -215,6 +217,13 @@ open class TraktProvider : MainAPI() { description = episode.overview, ).apply { this.addDate(episode.firstAired) + if (nextAir == null && this.date != null && this.date!! > unixTimeMS) { + nextAir = NextAiring( + episode = this.episode!!, + unixTime = this.date!!.div(1000L), + season = if (this.season == 1) null else this.season, + ) + } } ) } @@ -240,6 +249,7 @@ open class TraktProvider : MainAPI() { this.actors = actors this.comingSoon = isUpcoming(mediaDetails.released) //posterHeaders + this.nextAiring = nextAir this.seasonNames = seasonsNames this.backgroundPosterUrl = getOriginalWidthImageUrl(backDropUrl) this.contentRating = mediaDetails.certification From d0852449a50f548f88cd72785c338f2a5ad45184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Faruk=20Sancak?= Date: Mon, 27 May 2024 16:54:25 +0300 Subject: [PATCH 15/24] Extractor: Added FourPichive (#1103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🕊 --- .../cloudstream3/extractors/HotlingerExtractor.kt | 7 ++++++- .../java/com/lagradost/cloudstream3/utils/ExtractorApi.kt | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt b/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt index b557a53e..db721108 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/extractors/HotlingerExtractor.kt @@ -20,4 +20,9 @@ class PlayRu : ContentX() { class FourPlayRu : ContentX() { override var name = "FourPlayRu" override var mainUrl = "https://four.playru.net" -} \ No newline at end of file +} + +class FourPichive : ContentX() { + override var name = "FourPichive" + override var mainUrl = "https://four.pichive.online" +} 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 61cdd26a..c6cad804 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/ExtractorApi.kt @@ -110,6 +110,7 @@ import com.lagradost.cloudstream3.extractors.Hotlinger import com.lagradost.cloudstream3.extractors.FourCX import com.lagradost.cloudstream3.extractors.PlayRu import com.lagradost.cloudstream3.extractors.FourPlayRu +import com.lagradost.cloudstream3.extractors.FourPichive import com.lagradost.cloudstream3.extractors.HDMomPlayer import com.lagradost.cloudstream3.extractors.HDPlayerSystem import com.lagradost.cloudstream3.extractors.VideoSeyred @@ -748,6 +749,7 @@ val extractorApis: MutableList = arrayListOf( FourCX(), PlayRu(), FourPlayRu(), + FourPichive(), HDMomPlayer(), HDPlayerSystem(), VideoSeyred(), From 960f8449b7eda687f70b9cebbbfd76502cffa398 Mon Sep 17 00:00:00 2001 From: CranberrySoup <142951702+CranberrySoup@users.noreply.github.com> Date: Mon, 27 May 2024 13:54:51 +0000 Subject: [PATCH 16/24] Update ResultViewModel2.kt (#1102) --- .../com/lagradost/cloudstream3/ui/result/ResultViewModel2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e1a52074..4285feb1 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 @@ -1728,7 +1728,7 @@ class ResultViewModel2 : ViewModel() { txt(R.string.episode_action_cast_mirror) ) { (result, index) -> val host = device?.host ?: return@acquireSingleLink - val link = result.links.firstOrNull() ?: return@acquireSingleLink + val link = result.links.getOrNull(index) ?: return@acquireSingleLink FcastSession(host).use { session -> session.sendMessage( From 5502e478c4f9227e6da2f785b436e9c599d06fdf Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Mon, 27 May 2024 19:35:56 +0530 Subject: [PATCH 17/24] chore: update material,kotlin compiler,newpipe extractor,rhino-js,guava,corektx (#1091) --- app/build.gradle.kts | 12 +++++------- .../java/com/lagradost/cloudstream3/MainActivity.kt | 2 +- .../ui/player/DownloadedPlayerActivity.kt | 2 +- build.gradle.kts | 2 +- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f854865d..61a0634f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -164,7 +164,7 @@ dependencies { androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") // Android Core & Lifecycle - implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.core:core-ktx:1.13.1") implementation("androidx.appcompat:appcompat:1.6.1") implementation("androidx.navigation:navigation-ui-ktx:2.7.7") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") @@ -174,7 +174,7 @@ dependencies { // Design & UI implementation("jp.wasabeef:glide-transformations:4.3.0") implementation("androidx.preference:preference-ktx:1.2.1") - implementation("com.google.android.material:material:1.11.0") + implementation("com.google.android.material:material:1.12.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") @@ -185,7 +185,7 @@ dependencies { // For KSP -> Official Annotation Processors are Not Yet Supported for KSP ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0") - implementation("com.google.guava:guava:32.1.3-android") + implementation("com.google.guava:guava:33.2.0-android") implementation("dev.zacsweers.autoservice:auto-service-ksp:1.1.0") // Media 3 (ExoPlayer) @@ -202,7 +202,7 @@ dependencies { // PlayBack implementation("com.jaredrummler:colorpicker:1.1.0") // Subtitle Color Picker implementation("com.github.recloudstream:media-ffmpeg:1.1.0") // Custom FF-MPEG Lib for Audio Codecs - implementation("com.github.TeamNewPipe.NewPipeExtractor:NewPipeExtractor:6dc25f7b97") /* For Trailers + implementation("com.github.teamnewpipe:NewPipeExtractor:fafd471") /* For Trailers ^ Update to Latest Commits if Trailers Misbehave, github.com/TeamNewPipe/NewPipeExtractor/commits/dev */ implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding @@ -219,9 +219,7 @@ dependencies { implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview // Extensions & Other Libs - implementation("org.mozilla:rhino:1.7.13") /* run JavaScript - ^ Don't Bump RhinoJS to 1.7.14,`NoClassDefFoundError` Occurs and Trailers won't play (even with Desugaring) - NewPipeExtractor Issue */ + implementation("org.mozilla:rhino:1.7.15") // run JavaScript implementation("me.xdrop:fuzzywuzzy:1.4.0") // Library/Ext Searching with Levenshtein Distance implementation("com.github.LagradOst:SafeFile:0.0.6") // To Prevent the URI File Fu*kery implementation("org.conscrypt:conscrypt-android:2.5.2") // To Fix SSL Fu*kery on Android 9 diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 1ff0575b..cc2c99de 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -652,7 +652,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener, } } - override fun dispatchKeyEvent(event: KeyEvent?): Boolean { + override fun dispatchKeyEvent(event: KeyEvent): Boolean { val response = CommonActivity.dispatchKeyEvent(this, event) if (response != null) return response diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt index 1e2ea540..4d8860f8 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/DownloadedPlayerActivity.kt @@ -17,7 +17,7 @@ import com.lagradost.safefile.SafeFile const val DTAG = "PlayerActivity" class DownloadedPlayerActivity : AppCompatActivity() { - override fun dispatchKeyEvent(event: KeyEvent?): Boolean { + override fun dispatchKeyEvent(event: KeyEvent): Boolean { CommonActivity.dispatchKeyEvent(this, event)?.let { return it } diff --git a/build.gradle.kts b/build.gradle.kts index ab1918fe..34f141b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle:8.2.2") - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10") // Universal build config classpath("com.codingfeline.buildkonfig:buildkonfig-gradle-plugin:0.15.1") From dff56026de873d0f35cdd134decd1fa1008c0f5f Mon Sep 17 00:00:00 2001 From: KingLucius Date: Wed, 29 May 2024 23:39:55 +0300 Subject: [PATCH 18/24] SubDL Account login support (#1101) --- .../syncproviders/AccountManager.kt | 11 +- .../syncproviders/providers/Subdl.kt | 167 ++++++++++++++++-- .../ui/settings/SettingsAccount.kt | 2 + .../cloudstream3/utils/BackupUtils.kt | 2 + app/src/main/res/drawable/subdl_logo_big.xml | 10 ++ app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/settings_account.xml | 4 + 7 files changed, 182 insertions(+), 15 deletions(-) create mode 100644 app/src/main/res/drawable/subdl_logo_big.xml 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 e96499f0..1fd7900f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -14,7 +14,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val simklApi = SimklApi(0) val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() - val subDl = SubDL() + val subDlApi = SubDlApi(0) val localListApi = LocalList() // used to login via app intent @@ -26,7 +26,7 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { // this needs init with context and can be accessed in settings val accountManagers get() = listOf( - malApi, aniListApi, openSubtitlesApi, simklApi //nginxApi + malApi, aniListApi, openSubtitlesApi, subDlApi, simklApi //nginxApi ) // used for active syncing @@ -36,14 +36,17 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { ) val inAppAuths - get() = listOf(openSubtitlesApi)//, nginxApi) + get() = listOf( + openSubtitlesApi, + subDlApi + )//, nginxApi) val subtitleProviders get() = listOf( openSubtitlesApi, indexSubtitlesApi, // they got anti scraping measures in place :( addic7ed, - subDl + subDlApi ) const val appString = "cloudstreamapp" diff --git a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt index d25d3f22..29544e65 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/Subdl.kt @@ -1,21 +1,80 @@ package com.lagradost.cloudstream3.syncproviders.providers import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.AcraApplication.Companion.getKey +import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey +import com.lagradost.cloudstream3.AcraApplication.Companion.setKey +import com.lagradost.cloudstream3.ErrorLoadingException +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.subtitles.AbstractSubProvider +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.subtitles.AbstractSubApi import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities import com.lagradost.cloudstream3.subtitles.SubtitleResource +import com.lagradost.cloudstream3.syncproviders.AuthAPI.LoginInfo +import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI +import com.lagradost.cloudstream3.syncproviders.InAppAuthAPIManager -class SubDL : AbstractSubProvider { - //API Documentation: https://subdl.com/api-doc - val mainUrl = "https://subdl.com/" - val name = "SubDL" +class SubDlApi(index: Int) : InAppAuthAPIManager(index), AbstractSubApi { override val idPrefix = "subdl" + override val name = "SubDL" + override val icon = R.drawable.subdl_logo_big + override val requiresPassword = true + override val requiresEmail = true + override val createAccountUrl = "https://subdl.com/login" + companion object { - const val APIKEY = "zRJl5QA-8jNA2i0pE8cxANbEukANp7IM" - const val APIENDPOINT = "https://api.subdl.com/api/v1/subtitles" + const val APIURL = "https://api.subdl.com" + const val APIENDPOINT = "$APIURL/api/v1/subtitles" const val DOWNLOADENDPOINT = "https://dl.subdl.com" + const val SUBDL_SUBTITLES_USER_KEY: String = "subdl_user" + var currentSession: SubtitleOAuthEntity? = null + } + + override suspend fun initialize() { + currentSession = getAuthKey() + } + + override fun logOut() { + setAuthKey(null) + removeAccountKeys() + currentSession = getAuthKey() + } + override suspend fun login(data: InAppAuthAPI.LoginData): Boolean { + val email = data.email ?: throw ErrorLoadingException("Requires Email") + val password = data.password ?: throw ErrorLoadingException("Requires Password") + switchToNewAccount() + try { + if (initLogin(email, password)) { + registerAccount() + return true + } + } catch (e: Exception) { + logError(e) + switchToOldAccount() + } + switchToOldAccount() + return false + } + + override fun getLatestLoginData(): InAppAuthAPI.LoginData? { + val current = getAuthKey() ?: return null + return InAppAuthAPI.LoginData( + email = current.userEmail, + password = current.pass + ) + } + + override fun loginInfo(): LoginInfo? { + getAuthKey()?.let { user -> + return LoginInfo( + profilePicture = null, + name = user.name ?: user.userEmail, + accountIndex = accountIndex + ) + } + return null } override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List? { @@ -37,8 +96,8 @@ class SubDL : AbstractSubProvider { val searchQueryUrl = when (idQuery) { //Use imdb/tmdb id to search if its valid - null -> "$APIENDPOINT?api_key=$APIKEY&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery" - else -> "$APIENDPOINT?api_key=$APIKEY$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery" + null -> "$APIENDPOINT?api_key=${currentSession?.apiKey}&film_name=$queryText&languages=${query.lang}$epQuery$seasonQuery$yearQuery" + else -> "$APIENDPOINT?api_key=${currentSession?.apiKey}$idQuery&languages=${query.lang}$epQuery$seasonQuery$yearQuery" } val req = app.get( @@ -49,7 +108,7 @@ class SubDL : AbstractSubProvider { ) return req.parsedSafe()?.subtitles?.map { subtitle -> - val name = subtitle.releaseName + val lang = subtitle.lang.replaceFirstChar { it.uppercase() } val resEpNum = subtitle.episode ?: query.epNumber val resSeasonNum = subtitle.season ?: query.seasonNumber @@ -57,13 +116,14 @@ class SubDL : AbstractSubProvider { AbstractSubtitleEntities.SubtitleEntity( idPrefix = this.idPrefix, - name = name, + name = subtitle.releaseName, lang = lang, data = "${DOWNLOADENDPOINT}${subtitle.url}", type = type, source = this.name, epNumber = resEpNum, seasonNumber = resSeasonNum, + isHearingImpaired = subtitle.hearingImpaired ?: false, ) } } @@ -74,6 +134,88 @@ class SubDL : AbstractSubProvider { } } + private suspend fun initLogin(useremail: String, password: String): Boolean { + + val tokenResponse = app.post( + url = "$APIURL/login", + data = mapOf( + "email" to useremail, + "password" to password + ) + ).parsedSafe() + + if (tokenResponse?.token == null) return false + + val apiResponse = app.get( + url = "$APIURL/user/userApi", + headers = mapOf( + "Authorization" to "Bearer ${tokenResponse.token}" + ) + ).parsedSafe() + + if (apiResponse?.ok == false) return false + + setAuthKey( + SubtitleOAuthEntity( + userEmail = useremail, + pass = password, + name = tokenResponse.userData?.username ?: tokenResponse.userData?.name, + accessToken = tokenResponse.token, + apiKey = apiResponse?.apiKey + ) + ) + return true + } + + private fun getAuthKey(): SubtitleOAuthEntity? { + return getKey(accountId, SUBDL_SUBTITLES_USER_KEY) + } + + private fun setAuthKey(data: SubtitleOAuthEntity?) { + if (data == null) removeKey( + accountId, + SUBDL_SUBTITLES_USER_KEY + ) + currentSession = data + setKey(accountId, SUBDL_SUBTITLES_USER_KEY, data) + } + + data class SubtitleOAuthEntity( + @JsonProperty("userEmail") var userEmail: String, + @JsonProperty("pass") var pass: String, + @JsonProperty("name") var name: String? = null, + @JsonProperty("accessToken") var accessToken: String? = null, + @JsonProperty("apiKey") var apiKey: String? = null, + ) + + data class OAuthTokenResponse( + @JsonProperty("token") val token: String? = null, + @JsonProperty("userData") val userData: UserData? = null, + @JsonProperty("status") val status: Boolean? = null, + @JsonProperty("message") val message: String? = null, + ) + + data class UserData( + @JsonProperty("email") val email: String, + @JsonProperty("name") val name: String, + @JsonProperty("country") val country: String, + @JsonProperty("scStepCode") val scStepCode: String, + @JsonProperty("scVerified") val scVerified: Boolean, + @JsonProperty("username") val username: String? = null, + @JsonProperty("scUsername") val scUsername: String, + ) + + data class ApiKeyResponse( + @JsonProperty("ok") val ok: Boolean? = false, + @JsonProperty("api_key") val apiKey: String? = null, + @JsonProperty("usage") val usage: Usage? = null, + ) + + data class Usage( + @JsonProperty("total") val total: Long? = 0, + @JsonProperty("today") val today: Long? = 0, + ) + data class ApiResponse( @JsonProperty("status") val status: Boolean? = null, @JsonProperty("results") val results: List? = null, @@ -96,7 +238,10 @@ class SubDL : AbstractSubProvider { @JsonProperty("lang") val lang: String, @JsonProperty("author") val author: String? = null, @JsonProperty("url") val url: String? = null, + @JsonProperty("subtitlePage") val subtitlePage: String? = null, @JsonProperty("season") val season: Int? = null, @JsonProperty("episode") val episode: Int? = null, + @JsonProperty("language") val language: String? = null, + @JsonProperty("hi") val hearingImpaired: Boolean? = null, ) } \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt index f0d402da..27233525 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.aniList import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.malApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.openSubtitlesApi import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.simklApi +import com.lagradost.cloudstream3.syncproviders.AccountManager.Companion.subDlApi import com.lagradost.cloudstream3.syncproviders.AuthAPI import com.lagradost.cloudstream3.syncproviders.InAppAuthAPI import com.lagradost.cloudstream3.syncproviders.OAuth2API @@ -324,6 +325,7 @@ class SettingsAccount : PreferenceFragmentCompat(), BiometricAuthenticator.Biome R.string.anilist_key to aniListApi, R.string.simkl_key to simklApi, R.string.opensubtitles_key to openSubtitlesApi, + R.string.subdl_key to subDlApi, ) for ((key, api) in syncApis) { 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 279a0cb5..1d23e503 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BackupUtils.kt @@ -26,6 +26,7 @@ import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_T import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_UNIXTIME_KEY import com.lagradost.cloudstream3.syncproviders.providers.MALApi.Companion.MAL_USER_KEY import com.lagradost.cloudstream3.syncproviders.providers.OpenSubtitlesApi.Companion.OPEN_SUBTITLES_USER_KEY +import com.lagradost.cloudstream3.syncproviders.providers.SubDlApi.Companion.SUBDL_SUBTITLES_USER_KEY import com.lagradost.cloudstream3.ui.result.txt import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main @@ -64,6 +65,7 @@ object BackupUtils { PLUGINS_KEY_LOCAL, OPEN_SUBTITLES_USER_KEY, + SUBDL_SUBTITLES_USER_KEY, DOWNLOAD_EPISODE_CACHE, diff --git a/app/src/main/res/drawable/subdl_logo_big.xml b/app/src/main/res/drawable/subdl_logo_big.xml new file mode 100644 index 00000000..a6cbb311 --- /dev/null +++ b/app/src/main/res/drawable/subdl_logo_big.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8108623..44171dc5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -471,6 +471,7 @@ simkl_key mal_key opensubtitles_key + subdl_key nginx_key password123 Username diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml index 5cde06c4..d1d18a0f 100644 --- a/app/src/main/res/xml/settings_account.xml +++ b/app/src/main/res/xml/settings_account.xml @@ -17,6 +17,10 @@ android:icon="@drawable/open_subtitles_icon" android:key="@string/opensubtitles_key" /> + + Date: Sat, 1 Jun 2024 19:16:42 +0300 Subject: [PATCH 19/24] feat(TV UI): Account switch focus fix (#1112) --- app/src/main/res/layout/account_managment.xml | 14 +++++---- app/src/main/res/layout/account_single.xml | 29 ++++++++++--------- app/src/main/res/layout/account_switch.xml | 22 +++++++------- 3 files changed, 35 insertions(+), 30 deletions(-) diff --git a/app/src/main/res/layout/account_managment.xml b/app/src/main/res/layout/account_managment.xml index 389a3406..e7afb382 100644 --- a/app/src/main/res/layout/account_managment.xml +++ b/app/src/main/res/layout/account_managment.xml @@ -62,14 +62,16 @@ + android:id="@+id/account_switch_account" + android:text="@string/switch_account" + style="@style/SettingsItem" + android:focusable="true"/> + android:id="@+id/account_logout" + android:text="@string/logout" + style="@style/SettingsItem" + android:focusable="true"> diff --git a/app/src/main/res/layout/account_single.xml b/app/src/main/res/layout/account_single.xml index cbfb9f18..c4f7fa39 100644 --- a/app/src/main/res/layout/account_single.xml +++ b/app/src/main/res/layout/account_single.xml @@ -1,10 +1,11 @@ + xmlns:tools="http://schemas.android.com/tools" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:foreground="?android:attr/selectableItemBackgroundBorderless" + android:orientation="horizontal" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:focusable="true"> + android:id="@+id/account_profile_picture" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:ignore="ContentDescription" /> + android:foreground="@null" + android:id="@+id/account_name" + tools:text="Account 1" + style="@style/SettingsItem" /> diff --git a/app/src/main/res/layout/account_switch.xml b/app/src/main/res/layout/account_switch.xml index 659ad840..5153f0e3 100644 --- a/app/src/main/res/layout/account_switch.xml +++ b/app/src/main/res/layout/account_switch.xml @@ -7,18 +7,20 @@ android:layout_height="match_parent"> + android:id="@+id/account_list" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + android:background="?attr/primaryBlackBackground" + tools:listitem="@layout/account_single" + android:layout_width="match_parent" + android:layout_rowWeight="1" + android:layout_height="wrap_content" + android:focusable="true"/> + android:id="@+id/account_add" + android:text="@string/add_account" + style="@style/SettingsItem" + android:focusable="true"> From b3e3dadc72e61cd9925f721749578b074026f745 Mon Sep 17 00:00:00 2001 From: KingLucius Date: Sat, 1 Jun 2024 19:17:41 +0300 Subject: [PATCH 20/24] Remove IndexSubtitles provider (#1111) --- .../syncproviders/AccountManager.kt | 2 - .../providers/IndexSubtitleApi.kt | 265 ------------------ 2 files changed, 267 deletions(-) delete mode 100644 app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt 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 1fd7900f..a14f8438 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/syncproviders/AccountManager.kt @@ -12,7 +12,6 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val aniListApi = AniListApi(0) val openSubtitlesApi = OpenSubtitlesApi(0) val simklApi = SimklApi(0) - val indexSubtitlesApi = IndexSubtitleApi() val addic7ed = Addic7ed() val subDlApi = SubDlApi(0) val localListApi = LocalList() @@ -44,7 +43,6 @@ abstract class AccountManager(private val defIndex: Int) : AuthAPI { val subtitleProviders get() = listOf( openSubtitlesApi, - indexSubtitlesApi, // they got anti scraping measures in place :( addic7ed, subDlApi ) 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 deleted file mode 100644 index 5ca3f3d5..00000000 --- a/app/src/main/java/com/lagradost/cloudstream3/syncproviders/providers/IndexSubtitleApi.kt +++ /dev/null @@ -1,265 +0,0 @@ -package com.lagradost.cloudstream3.syncproviders.providers - -import android.util.Log -import com.lagradost.cloudstream3.TvType -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.imdbUrlToIdNullable -import com.lagradost.cloudstream3.subtitles.AbstractSubApi -import com.lagradost.cloudstream3.subtitles.AbstractSubtitleEntities -import com.lagradost.cloudstream3.utils.SubtitleHelper - -class IndexSubtitleApi : AbstractSubApi { - override val name = "IndexSubtitle" - override val idPrefix = "indexsubtitle" - override val requiresLogin = false - override val icon: Nothing? = null - override val createAccountUrl: Nothing? = null - - override fun loginInfo(): Nothing? = null - - override fun logOut() {} - - - companion object { - const val host = "https://indexsubtitle.com" - const val TAG = "INDEXSUBS" - - fun getOrdinal(num: Int?): String? { - return when (num) { - 1 -> "First" - 2 -> "Second" - 3 -> "Third" - 4 -> "Fourth" - 5 -> "Fifth" - 6 -> "Sixth" - 7 -> "Seventh" - 8 -> "Eighth" - 9 -> "Ninth" - 10 -> "Tenth" - 11 -> "Eleventh" - 12 -> "Twelfth" - 13 -> "Thirteenth" - 14 -> "Fourteenth" - 15 -> "Fifteenth" - 16 -> "Sixteenth" - 17 -> "Seventeenth" - 18 -> "Eighteenth" - 19 -> "Nineteenth" - 20 -> "Twentieth" - 21 -> "Twenty-First" - 22 -> "Twenty-Second" - 23 -> "Twenty-Third" - 24 -> "Twenty-Fourth" - 25 -> "Twenty-Fifth" - 26 -> "Twenty-Sixth" - 27 -> "Twenty-Seventh" - 28 -> "Twenty-Eighth" - 29 -> "Twenty-Ninth" - 30 -> "Thirtieth" - 31 -> "Thirty-First" - 32 -> "Thirty-Second" - 33 -> "Thirty-Third" - 34 -> "Thirty-Fourth" - 35 -> "Thirty-Fifth" - else -> null - } - } - } - - private fun fixUrl(url: String): String { - if (url.startsWith("http")) { - return url - } - if (url.isEmpty()) { - return "" - } - - val startsWithNoHttp = url.startsWith("//") - if (startsWithNoHttp) { - return "https:$url" - } else { - if (url.startsWith('/')) { - return host + url - } - return "$host/$url" - } - } - - private fun isRightEps(text: String, seasonNum: Int?, epNum: Int?): Boolean { - val FILTER_EPS_REGEX = - Regex("(?i)((Chapter\\s?0?${epNum})|((Season)?\\s?0?${seasonNum}?\\s?(Episode)\\s?0?${epNum}[^0-9]))|(?i)((S?0?${seasonNum}?E0?${epNum}[^0-9])|(0?${seasonNum}[a-z]0?${epNum}[^0-9]))") - return text.contains(FILTER_EPS_REGEX) - } - - private fun haveEps(text: String): Boolean { - val HAVE_EPS_REGEX = - Regex("(?i)((Chapter\\s?0?\\d)|((Season)?\\s?0?\\d?\\s?(Episode)\\s?0?\\d))|(?i)((S?0?\\d?E0?\\d)|(0?\\d[a-z]0?\\d))") - return text.contains(HAVE_EPS_REGEX) - } - - override suspend fun search(query: AbstractSubtitleEntities.SubtitleSearch): List { - val imdbId = query.imdbId?.replace("tt", "")?.toLong() ?: 0 - val lang = query.lang - val queryLang = SubtitleHelper.fromTwoLettersToLanguage(lang.toString()) - val queryText = query.query - val epNum = query.epNumber ?: 0 - val seasonNum = query.seasonNumber ?: 0 - val yearNum = query.year ?: 0 - - val urlItems = ArrayList() - - fun cleanResources( - results: MutableList, - name: String, - link: String - ) { - results.add( - AbstractSubtitleEntities.SubtitleEntity( - idPrefix = idPrefix, - name = name, - lang = queryLang.toString(), - data = link, - source = this.name, - type = if (seasonNum > 0) TvType.TvSeries else TvType.Movie, - epNumber = epNum, - seasonNumber = seasonNum, - year = yearNum, - ) - ) - } - - val document = app.get("$host/?search=$queryText").document - - document.select("div.my-3.p-3 div.media").map { block -> - if (seasonNum > 0) { - val name = block.select("strong.text-primary, strong.text-info").text().trim() - val season = getOrdinal(seasonNum) - if ((block.selectFirst("a")?.attr("href") - ?.contains( - "$season", - ignoreCase = true - )!! || name.contains( - "$season", - ignoreCase = true - )) && name.contains(queryText, ignoreCase = true) - ) { - block.select("div.media").mapNotNull { - urlItems.add( - fixUrl( - it.selectFirst("a")!!.attr("href") - ) - ) - } - } - } else { - if (block.selectFirst("strong")!!.text().trim() - .matches(Regex("(?i)^$queryText\$")) - ) { - if (block.select("span[title=Release]").isNullOrEmpty()) { - block.select("div.media").mapNotNull { - val urlItem = fixUrl( - it.selectFirst("a")!!.attr("href") - ) - val itemDoc = app.get(urlItem).document - val id = imdbUrlToIdNullable( - itemDoc.selectFirst("div.d-flex span.badge.badge-primary")?.parent() - ?.attr("href") - )?.toLongOrNull() - val year = itemDoc.selectFirst("div.d-flex span.badge.badge-success") - ?.ownText() - ?.trim().toString() - Log.i(TAG, "id => $id \nyear => $year||$yearNum") - if (imdbId > 0) { - if (id == imdbId) { - urlItems.add(urlItem) - } - } else { - if (year.contains("$yearNum")) { - urlItems.add(urlItem) - } - } - } - } else { - if (block.select("span[title=Release]").text().trim() - .contains("$yearNum") - ) { - block.select("div.media").mapNotNull { - urlItems.add( - fixUrl( - it.selectFirst("a")!!.attr("href") - ) - ) - } - } - } - } - } - } - Log.i(TAG, "urlItems => $urlItems") - val results = mutableListOf() - - urlItems.forEach { url -> - val request = app.get(url) - 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() - .trim() - .contains("$queryLang") - ) { - var name = block.select("strong.text-primary, strong.text-info").text().trim() - val link = fixUrl(block.selectFirst("a")!!.attr("href")) - if (seasonNum > 0) { - when { - isRightEps(name, seasonNum, epNum) -> { - cleanResources(results, name, link) - } - !(haveEps(name)) -> { - name = "$name (S${seasonNum}:E${epNum})" - cleanResources(results, name, link) - } - } - } else { - cleanResources(results, name, link) - } - } - } - } - } - return results - } - - override suspend fun load(data: AbstractSubtitleEntities.SubtitleEntity): String? { - val seasonNum = data.seasonNumber - val epNum = data.epNumber - - val req = app.get(data.data) - - if (req.isSuccessful) { - val document = req.document - val link = if (document.select("div.my-3.p-3 div.media").size == 1) { - fixUrl( - document.selectFirst("div.my-3.p-3 div.media a")!!.attr("href") - ) - } else { - document.select("div.my-3.p-3 div.media").firstNotNullOf { block -> - val name = - block.selectFirst("strong.d-block")?.text()?.trim().toString() - if (seasonNum!! > 0) { - if (isRightEps(name, seasonNum, epNum)) { - fixUrl(block.selectFirst("a")!!.attr("href")) - } else { - null - } - } else { - fixUrl(block.selectFirst("a")!!.attr("href")) - } - } - } - return link - } - - return null - - } - -} \ No newline at end of file From 9bebfe459005021970e25bd5ca38816fa5a66ba4 Mon Sep 17 00:00:00 2001 From: int3debug <164035730+int3debug@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:07:54 +0200 Subject: [PATCH 21/24] feature(ui): hide NSFW plugins (#1117) Hide NSFW plugins if Settings / Providers NSFW is disabled --- .../cloudstream3/ui/settings/extensions/PluginsViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt index 151c8d57..2b026e0d 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/PluginsViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.lagradost.cloudstream3.CommonActivity.showToast +import com.lagradost.cloudstream3.MainAPI.Companion.settingsForProvider import com.lagradost.cloudstream3.PROVIDER_STATUS_DOWN import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.amap @@ -181,8 +182,11 @@ class PluginsViewModel : ViewModel() { } private suspend fun updatePluginListPrivate(context: Context, repositoryUrl: String) { + val isAdult = settingsForProvider.enableAdult val plugins = getPlugins(repositoryUrl) - val list = plugins.map { plugin -> + val list = plugins.filter { + return@filter !(it.second.tvTypes?.contains("NSFW") == true && !isAdult) + }.map { plugin -> PluginViewData(plugin, isDownloaded(context, plugin.second.internalName, plugin.first)) } From 0391a3b89cb3d4266afc1e1a710366b9266f1241 Mon Sep 17 00:00:00 2001 From: int3debug <164035730+int3debug@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:09:05 +0200 Subject: [PATCH 22/24] feature(ui): added wikipedia to links (#1119) --- app/src/main/res/values/strings.xml | 1 + app/src/main/res/xml/settings_general.xml | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 44171dc5..deee5ad2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -774,4 +774,5 @@ Audio Book Media Reset + CloudStream Wiki \ No newline at end of file diff --git a/app/src/main/res/xml/settings_general.xml b/app/src/main/res/xml/settings_general.xml index cdda6d85..853bbda1 100644 --- a/app/src/main/res/xml/settings_general.xml +++ b/app/src/main/res/xml/settings_general.xml @@ -86,6 +86,14 @@ android:action="android.intent.action.VIEW" android:data="https://discord.gg/5Hus6fM" /> + + + \ No newline at end of file From 358a20eb7786daa661ef60d20caca67d8eec105f Mon Sep 17 00:00:00 2001 From: IndusAryan <125901294+IndusAryan@users.noreply.github.com> Date: Thu, 6 Jun 2024 02:48:33 +0530 Subject: [PATCH 23/24] chore: refactor gradlelocalproperties and update gradle plugin (#957) --- app/build.gradle.kts | 2 +- build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 61a0634f..21f22dd1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,7 +69,7 @@ android { resValue("bool", "is_prerelease", "false") // Reads local.properties - val localProperties = gradleLocalProperties(rootDir) + val localProperties = gradleLocalProperties(rootDir, providers) buildConfigField( "long", diff --git a/build.gradle.kts b/build.gradle.kts index 34f141b4..ba87b6f4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath("com.android.tools.build:gradle:8.2.2") + classpath("com.android.tools.build:gradle:8.4.0") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10") // Universal build config diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fc2d0f86..2968a1b2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Apr 30 17:11:15 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME From 7eec0eff02b1e7f8bd18a948515adae5fdc13f9e Mon Sep 17 00:00:00 2001 From: firelight <147925818+fire-light42@users.noreply.github.com> Date: Wed, 5 Jun 2024 23:41:06 +0200 Subject: [PATCH 24/24] Revert "chore: refactor gradlelocalproperties and update gradle plugin (#957)" (#1120) This reverts commit 358a20eb7786daa661ef60d20caca67d8eec105f. --- app/build.gradle.kts | 2 +- build.gradle.kts | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 21f22dd1..61a0634f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -69,7 +69,7 @@ android { resValue("bool", "is_prerelease", "false") // Reads local.properties - val localProperties = gradleLocalProperties(rootDir, providers) + val localProperties = gradleLocalProperties(rootDir) buildConfigField( "long", diff --git a/build.gradle.kts b/build.gradle.kts index ba87b6f4..34f141b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath("com.android.tools.build:gradle:8.4.0") + classpath("com.android.tools.build:gradle:8.2.2") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23") classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.9.10") // Universal build config diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2968a1b2..fc2d0f86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Apr 30 17:11:15 CEST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME