From 73167740e817fdfcc3fa6fa8bb4ff166103cc7e1 Mon Sep 17 00:00:00 2001 From: hexated Date: Sat, 15 Oct 2022 22:26:51 +0700 Subject: [PATCH] added SoraStream --- SoraStream/Icon.png | Bin 0 -> 32119 bytes SoraStream/build.gradle.kts | 28 ++ SoraStream/src/main/AndroidManifest.xml | 2 + .../src/main/kotlin/com/hexated/SoraStream.kt | 338 ++++++++++++++++++ .../kotlin/com/hexated/SoraStreamPlugin.kt | 14 + 5 files changed, 382 insertions(+) create mode 100644 SoraStream/Icon.png create mode 100644 SoraStream/build.gradle.kts create mode 100644 SoraStream/src/main/AndroidManifest.xml create mode 100644 SoraStream/src/main/kotlin/com/hexated/SoraStream.kt create mode 100644 SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt diff --git a/SoraStream/Icon.png b/SoraStream/Icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3b3a2250a78988c8b09a94be405dbd164616f9b4 GIT binary patch literal 32119 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 literal 0 HcmV?d00001 diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts new file mode 100644 index 00000000..55c39336 --- /dev/null +++ b/SoraStream/build.gradle.kts @@ -0,0 +1,28 @@ +// use an integer for version numbers +version = 1 + + +cloudstream { + language = "en" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Hexated", "Sora") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "AsianDrama", + "TvSeries", + "Anime", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=kisskh.me&sz=%size%" +} \ No newline at end of file diff --git a/SoraStream/src/main/AndroidManifest.xml b/SoraStream/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c98063f8 --- /dev/null +++ b/SoraStream/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt new file mode 100644 index 00000000..c3341e1b --- /dev/null +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -0,0 +1,338 @@ +package com.hexated + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.metaproviders.TmdbProvider +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import kotlin.math.roundToInt + +class SoraStream : TmdbProvider() { + override var name = "SoraStream" + override val hasMainPage = true + override val hasDownloadSupport = true + override val instantLinkLoading = true + override val useMetaLoadResponse = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.Anime, + ) + + /** AUTHOR : Hexated & Sora */ + companion object { + private const val tmdbAPI = "https://api.themoviedb.org/3" + private const val apiKey = "b030404650f279792a8d3287232358e3" // PLEASE DON'T STEAL + private var mainAPI = base64Decode("aHR0cHM6Ly94cHdhdGNoLnZlcmNlbC5hcHA=") + private var mainServerAPI = base64Decode("aHR0cHM6Ly9zb3JhLW1vdmllLnZlcmNlbC5hcHA=") + + fun getType(t: String?): TvType { + return when (t) { + "movie" -> TvType.Movie + else -> TvType.TvSeries + } + } + + fun getActorRole(t: String?): ActorRole { + return when (t) { + "Acting" -> ActorRole.Main + else -> ActorRole.Background + } + } + + + fun getStatus(t: String?): ShowStatus { + return when (t) { + "Returning Series" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + } + + override val mainPage = mainPageOf( + "$tmdbAPI/movie/popular?api_key=$apiKey®ion=&page=" to "Popular Movies", + "$tmdbAPI/tv/popular?api_key=$apiKey®ion=&page=" to "Popular TV Shows", + "$tmdbAPI/movie/top_rated?api_key=$apiKey®ion=&page=" to "Top Rated Movies", + "$tmdbAPI/tv/top_rated?api_key=$apiKey®ion=&page=" to "Top Rated TV Shows", +// "$tmdbAPI/discover/tv?api_key=$apiKey&with_keywords=210024|222243&page=" to "Anime", +// "$tmdbAPI/discover/movie?api_key=$apiKey&with_keywords=210024|222243&page=" to "Anime Movies", + ) + + private fun getImageUrl(link: String?): String? { + if (link == null) return null + return if (link.startsWith("/")) "https://image.tmdb.org/t/p/w500/$link" else link + } + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val type = if (request.data.contains("/movie")) "movie" else "tv" + val home = app.get(request.data + page) + .parsedSafe()?.results + ?.mapNotNull { media -> + media.toSearchResponse(type) + } ?: throw ErrorLoadingException("Invalid Json reponse") + return newHomePageResponse(request.name, home) + } + + private fun Media.toSearchResponse(type: String? = null): SearchResponse? { + return newMovieSearchResponse( + title ?: name ?: originalTitle ?: return null, + Data(id = id, type = mediaType ?: type).toJson(), + TvType.Movie, + ) { + this.posterUrl = getImageUrl(posterPath) + } + } + + override suspend fun search(query: String): List { + return app.get( + "$tmdbAPI/search/multi?api_key=$apiKey&language=en-US&query=$query&page=1&include_adult=false", + referer = "$mainAPI/" + ).parsedSafe()?.results?.mapNotNull { media -> + media.toSearchResponse() + } ?: throw ErrorLoadingException("Invalid Json reponse") + } + + override suspend fun load(url: String): LoadResponse? { + val data = parseJson(url) + val buildId = + app.get("$mainAPI/").text.substringAfterLast("\"buildId\":\"").substringBefore("\",") + val responses = + app.get("$mainAPI/_next/data/$buildId/${data.type}/${data.id}.json?id=${data.id}") + .parsedSafe()?.pageProps + ?: throw ErrorLoadingException("Invalid Json Response") + val res = responses.result ?: return null + val type = getType(data.type) + val actors = responses.cast?.mapNotNull { cast -> + ActorData( + Actor( + cast.name ?: cast.originalName ?: return@mapNotNull null, + getImageUrl(cast.profilePath) + ), + getActorRole(cast.knownForDepartment) + ) + } ?: return null + val recommendations = + responses.recommandations?.mapNotNull { media -> media.toSearchResponse() } + + return if (type == TvType.TvSeries) { + val episodes = mutableListOf() + res.seasons?.apmap { season -> + app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey") + .parsedSafe()?.episodes?.map { eps -> + episodes.add(Episode( + LinkData( + data.id, + responses.imdbId, + data.type, + eps.seasonNumber, + eps.episodeNumber + ).toJson(), + name = eps.name, + season = eps.seasonNumber, + episode = eps.episodeNumber, + posterUrl = getImageUrl(eps.stillPath), + rating = eps.voteAverage?.times(10)?.roundToInt(), + description = eps.overview + ).apply { + this.addDate(eps.airDate) + }) + } + } + newTvSeriesLoadResponse( + res.title ?: res.name ?: res.originalTitle ?: res.originalName ?: return null, + url, + TvType.TvSeries, + episodes + ) { + this.posterUrl = getImageUrl(res.posterPath) + this.year = + (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull() + this.plot = res.overview + this.tags = res.genres?.mapNotNull { it.name } + this.showStatus = getStatus(res.status) + this.recommendations = recommendations + this.actors = actors + } + } else { + newMovieLoadResponse( + res.title ?: res.name ?: res.originalTitle ?: res.originalName ?: return null, + url, + TvType.Movie, + LinkData( + data.id, + responses.imdbId, + data.type, + ).toJson(), + ) { + this.posterUrl = getImageUrl(res.posterPath) + this.year = + (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull() + this.plot = res.overview + this.tags = res.genres?.mapNotNull { it.name } + this.recommendations = recommendations + this.actors = actors + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val res = parseJson(data) + val query = if (res.type == "tv") { + "$mainServerAPI/tv-shows/${res.id}/season/${res.season}/episode/${res.episode}?_data=routes/tv-shows/\$tvId.season.\$seasonId.episode.\$episodeId" + } else { + "$mainServerAPI/movies/${res.id}/watch?_data=routes/movies/\$movieId.watch" + } + val referer = if (res.type == "tv") { + "$mainServerAPI/tv-shows/${res.id}/season/${res.season}/episode/${res.episode}" + } else { + "$mainServerAPI/movies/${res.id}/watch" + } + + val json = app.get( + query, + referer = referer + ).parsedSafe() + + json?.sources?.map { source -> + callback.invoke( + ExtractorLink( + this.name, + this.name, + source.url ?: return@map null, + "$mainServerAPI/", + source.quality?.toIntOrNull() ?: Qualities.Unknown.value, + isM3u8 = source.isM3U8, + headers = mapOf("Origin" to mainServerAPI) + ) + ) + } + + json?.subtitles?.map { sub -> + subtitleCallback.invoke( + SubtitleFile( + sub.lang.toString(), + sub.url ?: return@map null + ) + ) + } + + return true + } + + private data class LinkData( + val id: Int? = null, + val imdbId: String? = null, + val type: String? = null, + val season: Int? = null, + val episode: Int? = null, + ) + + data class Data( + val id: Int? = null, + val type: String? = null, + ) + + data class Subtitles( + @JsonProperty("url") val url: String? = null, + @JsonProperty("lang") val lang: String? = null, + ) + + data class Sources( + @JsonProperty("url") val url: String? = null, + @JsonProperty("quality") val quality: String? = null, + @JsonProperty("isM3U8") val isM3U8: Boolean = true, + ) + + data class LoadLinks( + @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + ) + + data class Results( + @JsonProperty("results") val results: ArrayList? = arrayListOf(), + ) + + data class Media( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("original_title") val originalTitle: String? = null, + @JsonProperty("media_type") val mediaType: String? = null, + @JsonProperty("poster_path") val posterPath: String? = null, + ) + + data class Genres( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + ) + + data class Seasons( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("season_number") val seasonNumber: Int? = null, + ) + + data class Cast( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("original_name") val originalName: String? = null, + @JsonProperty("character") val character: String? = null, + @JsonProperty("known_for_department") val knownForDepartment: String? = null, + @JsonProperty("profile_path") val profilePath: String? = null, + ) + + data class Episodes( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("overview") val overview: String? = null, + @JsonProperty("air_date") val airDate: String? = null, + @JsonProperty("still_path") val stillPath: String? = null, + @JsonProperty("vote_average") val voteAverage: Double? = null, + @JsonProperty("episode_number") val episodeNumber: Int? = null, + @JsonProperty("season_number") val seasonNumber: Int? = null, + ) + + data class MediaDetailEpisodes( + @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), + ) + + data class MediaDetail( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("imdb_id") val imdbId: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("original_title") val originalTitle: String? = null, + @JsonProperty("original_name") val originalName: String? = null, + @JsonProperty("poster_path") val posterPath: String? = null, + @JsonProperty("release_date") val releaseDate: String? = null, + @JsonProperty("first_air_date") val firstAirDate: String? = null, + @JsonProperty("overview") val overview: String? = null, + @JsonProperty("status") val status: String? = null, + @JsonProperty("genres") val genres: ArrayList? = arrayListOf(), + @JsonProperty("seasons") val seasons: ArrayList? = arrayListOf(), + ) + + data class PageProps( + @JsonProperty("id") val id: String? = null, + @JsonProperty("imdb") val imdbId: String? = null, + @JsonProperty("result") val result: MediaDetail? = null, + @JsonProperty("recommandations") val recommandations: ArrayList? = arrayListOf(), + @JsonProperty("cast") val cast: ArrayList? = arrayListOf(), + ) + + data class Detail( + @JsonProperty("pageProps") val pageProps: PageProps? = null, + ) + +} diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt new file mode 100644 index 00000000..bdf4216e --- /dev/null +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt @@ -0,0 +1,14 @@ + +package com.hexated + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class SoraStreamPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(SoraStream()) + } +} \ No newline at end of file