From 81c15c46bc56268941c8549c47abc9df05e89508 Mon Sep 17 00:00:00 2001 From: Cloudburst <18114966+C10udburst@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:57:31 +0200 Subject: [PATCH] add stremio --- StremioProvider/build.gradle.kts | 24 +++ StremioProvider/src/main/AndroidManifest.xml | 2 + .../kotlin/com/lagradost/StremioProvider.kt | 178 ++++++++++++++++++ .../com/lagradost/StremioProviderPlugin.kt | 14 ++ 4 files changed, 218 insertions(+) create mode 100644 StremioProvider/build.gradle.kts create mode 100644 StremioProvider/src/main/AndroidManifest.xml create mode 100644 StremioProvider/src/main/kotlin/com/lagradost/StremioProvider.kt create mode 100644 StremioProvider/src/main/kotlin/com/lagradost/StremioProviderPlugin.kt diff --git a/StremioProvider/build.gradle.kts b/StremioProvider/build.gradle.kts new file mode 100644 index 0000000..a810295 --- /dev/null +++ b/StremioProvider/build.gradle.kts @@ -0,0 +1,24 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "en" + // All of these properties are optional, you can safely remove them + description = "Allows you to use stremio addons. Requires setup. (Torrents and old api does not work)" + + // description = "Lorem Ipsum" + authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf("Others") + + iconUrl = "https://www.google.com/s2/favicons?domain=www.stremio.com&sz=%size%" +} \ No newline at end of file diff --git a/StremioProvider/src/main/AndroidManifest.xml b/StremioProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/StremioProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/StremioProvider/src/main/kotlin/com/lagradost/StremioProvider.kt b/StremioProvider/src/main/kotlin/com/lagradost/StremioProvider.kt new file mode 100644 index 0000000..a72e1ef --- /dev/null +++ b/StremioProvider/src/main/kotlin/com/lagradost/StremioProvider.kt @@ -0,0 +1,178 @@ +package com.lagradost + +import android.util.Log +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson + +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.loadExtractor +import org.json.JSONObject + +class StremioProvider : MainAPI() { + override var mainUrl = "https://stremio.github.io/stremio-static-addon-example" + override var name = "Stremio example" + override val supportedTypes = setOf(TvType.Others) + override val hasMainPage = true + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse? { + val res = tryParseJson(app.get("${mainUrl}/manifest.json").text) ?: return null + val lists = mutableListOf() + res.catalogs.forEach { catalog -> + catalog.toHomePageList(this)?.let { + lists.add(it) + } + } + return HomePageResponse( + lists, + false + ) + } + + override suspend fun load(url: String): LoadResponse? { + val res = tryParseJson(url) ?: throw RuntimeException(url) + return res.toLoadResponse(this) + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val res = tryParseJson(app.get(data).text) ?: return false + res.streams.forEach { stream -> + stream.runCallback(subtitleCallback, callback) + } + return true + } + + private data class Manifest(val catalogs: List) + private data class Catalog( + var name: String?, + val id: String, + val type: String?, + val types: MutableList = mutableListOf() + ) { + init { + if (type != null) types.add(type) + } + + suspend fun toHomePageList(provider: StremioProvider): HomePageList? { + val entries = mutableListOf() + types.forEach { type -> + val res = tryParseJson(app.get("${provider.mainUrl}/catalog/$type/$id.json").text) ?: return@forEach + res.metas.forEach { entry -> + entries.add(entry.toSearchResponse(provider)) + } + } + return HomePageList( + name ?: id, + entries + ) + } + } + + private data class CatalogResponse(val metas: List) + private data class CatalogEntry( + val name: String, + val id: String, + val poster: String?, + val description: String?, + val type: String? + ) { + fun toSearchResponse(provider: StremioProvider): SearchResponse { + return provider.newMovieSearchResponse( + name, + this.toJson(), + type?.let { getType(it) } ?: TvType.Others + ) { + posterUrl = poster + } + } + suspend fun toLoadResponse(provider: StremioProvider): LoadResponse { + return provider.newMovieLoadResponse( + name, + "${provider.mainUrl}/meta/$type/$id.json", + getType(type), + "${provider.mainUrl}/stream/$type/$id.json" + ) { + posterUrl = poster + plot = description + } + } + } + + private data class StreamsResponse(val streams: List) + private data class Stream( + val name: String?, + val title: String?, + val url: String?, + val ytId: String?, + val externalUrl: String?, + val behaviorHints: JSONObject? + ) { + suspend fun runCallback(subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit) { + if (url != null) { + var referer: String? = null + try { + val headers = ((behaviorHints?.get("proxyHeaders") as? JSONObject) + ?.get("request") as? JSONObject) + referer = headers?.get("referer") as? String ?: headers?.get("origin") as? String + } catch (ex: Throwable) { + Log.e("Stremio", Log.getStackTraceString(ex)) + } + + if (url.endsWith(".m3u8")) { + callback.invoke( + ExtractorLink( + name ?: "", + title ?: name ?: "", + url, + referer ?: "", + Qualities.Unknown.value, + isM3u8 = true + )) + } else { + callback.invoke( + ExtractorLink( + name ?: "", + title ?: name ?: "", + url, + referer ?: "", + Qualities.Unknown.value, + isM3u8 = false + )) + } + } + if (ytId != null) { + loadExtractor("https://www.youtube.com/watch?v=$ytId", subtitleCallback, callback) + } + if (externalUrl != null) { + loadExtractor(externalUrl, subtitleCallback, callback) + } + } + } + + companion object { + private val typeAliases = hashMapOf( + "tv" to TvType.TvSeries, + "series" to TvType.TvSeries, + "channel" to TvType.Live, + "adult" to TvType.NSFW + ) + + fun getType(t_str: String?): TvType { + if (t_str == null) return TvType.Others + typeAliases[t_str.lowercase()]?.let { + return it + } + for (t_enum in TvType.values()) { + if (t_enum.toString().lowercase() == t_str.lowercase()) + return t_enum + } + return TvType.Others + } + } +} diff --git a/StremioProvider/src/main/kotlin/com/lagradost/StremioProviderPlugin.kt b/StremioProvider/src/main/kotlin/com/lagradost/StremioProviderPlugin.kt new file mode 100644 index 0000000..60732f5 --- /dev/null +++ b/StremioProvider/src/main/kotlin/com/lagradost/StremioProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class StremioProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(StremioProvider()) + } +} \ No newline at end of file