From 1a4d4756b3004b8519032e5a0437da51d9046d09 Mon Sep 17 00:00:00 2001 From: hexated Date: Sat, 19 Nov 2022 11:37:28 +0700 Subject: [PATCH] added new source into SoraExtractor --- SoraStream/build.gradle.kts | 2 +- .../main/kotlin/com/hexated/SoraExtractor.kt | 85 ++++++++++++++++--- .../src/main/kotlin/com/hexated/SoraStream.kt | 50 +++++++++-- 3 files changed, 115 insertions(+), 22 deletions(-) diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 990f78ea..b35f66bd 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 31 +version = 32 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 2e14ccf9..59a95533 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -1046,20 +1046,8 @@ object SoraExtractor : SoraStream() { link?.substringBefore("=http") ?: return@apmap null, "$kissKhAPI/", subtitleCallback, - ) { links -> - callback.invoke( - ExtractorLink( - "StreamSS", - "StreamSS", - links.url, - links.referer, - links.quality, - links.isM3u8, - links.headers, - links.extractorData - ) - ) - } + callback + ) } } } @@ -1131,6 +1119,75 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeLing( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val fixTitle = title?.replace("–", "-") + val url = if(season == null) { + "$lingAPI/en/videos/films/?title=$fixTitle" + } else { + "$lingAPI/en/videos/serials/?title=$fixTitle" + } + + val scriptData = app.get(url).document.select("div.blk.padding_b0 div.col-sm-30").map { + Triple( + it.selectFirst("div.video-body h5")?.text(), + it.selectFirst("div.video-body > p")?.text(), + it.selectFirst("div.video-body a")?.attr("href"), + ) + } + + val script = if (scriptData.size == 1) { + scriptData.first() + } else { + scriptData.find { + it.first?.contains("$fixTitle", true) == true && it.second?.contains("$year") == true + } + } + + val doc = app.get(fixUrl(script?.third ?: return, lingAPI)).document + val iframe = (if(season == null) { + doc.selectFirst("a.video-js.vjs-default-skin")?.attr("data-href") + } else { + doc.select("div.blk div#tab_$season li")[episode!!.minus(1)].select("h5 a").attr("data-href") + })?.let { fixUrl(it, lingAPI) } + + val source = app.get(iframe ?: return) + val link = Regex("((https:|http:)//.*\\.mp4)").find(source.text)?.value ?: return + val quality = when { + link.contains("1080p") -> Qualities.P1080.value + link.contains("720p") -> Qualities.P720.value + else -> Qualities.Unknown.value + } + callback.invoke( + ExtractorLink( + "Ling", + "Ling", + link, + "$lingAPI/", + quality, + headers = mapOf( + "Range" to "bytes=0-" + ) + ) + ) + + source.document.select("div#player-tracks track").map { + subtitleCallback.invoke( + SubtitleFile( + SubtitleHelper.fromTwoLettersToLanguage(it.attr("srclang")) ?: return@map null, + it.attr("src") + ) + ) + } + + } + } data class FilmxyCookies( diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 796dd8de..ff5cb9c7 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -22,6 +22,7 @@ import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.metaproviders.TmdbProvider import com.hexated.SoraExtractor.invoZoro +import com.hexated.SoraExtractor.invokeLing import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink @@ -67,6 +68,7 @@ open class SoraStream : TmdbProvider() { const val consumetFlixhqAPI = "https://api.consumet.org/movies/flixhq" const val consumetZoroAPI = "https://api.consumet.org/anime/zoro" const val kissKhAPI = "https://kisskh.me" + const val lingAPI = "https://ling-online.net" fun getType(t: String?): TvType { return when (t) { @@ -181,9 +183,13 @@ open class SoraStream : TmdbProvider() { ?: throw ErrorLoadingException("Invalid Json Response") val res = responses.result ?: return null val title = res.title ?: res.name ?: return null + val poster = getOriImageUrl(res.backdropPath) val orgTitle = res.originalTitle ?: res.originalName ?: return null val type = getType(data.type) val year = (res.releaseDate ?: res.firstAirDate)?.split("-")?.first()?.toIntOrNull() + val genres = res.genres?.mapNotNull { it.name } + val show = + if (genres?.contains("Animation") == true && res.original_language == "ja") "Anime" else "Series/Movies" val actors = responses.cast?.mapNotNull { cast -> ActorData( @@ -216,6 +222,8 @@ open class SoraStream : TmdbProvider() { title = title, year = season.airDate?.split("-")?.first()?.toIntOrNull(), orgTitle = orgTitle, + show = show, + airedYear = year ).toJson(), name = eps.name, season = eps.seasonNumber, @@ -234,10 +242,10 @@ open class SoraStream : TmdbProvider() { TvType.TvSeries, episodes ) { - this.posterUrl = getOriImageUrl(res.backdropPath) + this.posterUrl = poster this.year = year this.plot = res.overview - this.tags = res.genres?.mapNotNull { it.name } + this.tags = genres this.showStatus = getStatus(res.status) this.recommendations = recommendations this.actors = actors @@ -255,12 +263,13 @@ open class SoraStream : TmdbProvider() { title = title, year = year, orgTitle = orgTitle, + show = show, ).toJson(), ) { - this.posterUrl = getOriImageUrl(res.backdropPath) + this.posterUrl = poster this.year = year this.plot = res.overview - this.tags = res.genres?.mapNotNull { it.name } + this.tags = genres this.recommendations = recommendations this.actors = actors addTrailer(trailer) @@ -362,7 +371,7 @@ open class SoraStream : TmdbProvider() { // ) // }, { - if (res.season != null) invoZoro( + if (res.season != null && res.show == "Anime") invoZoro( res.id, res.season, res.episode, @@ -406,14 +415,38 @@ open class SoraStream : TmdbProvider() { invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) }, { - invokeXmovies(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + invokeXmovies( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { - invokeFlixhq(res.title, res.year, res.season, res.episode, subtitleCallback, callback) + invokeFlixhq( + res.title, + res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) }, { invoKisskh(res.title, res.season, res.episode, subtitleCallback, callback) }, + { + invokeLing( + res.title, + res.airedYear ?: res.year, + res.season, + res.episode, + subtitleCallback, + callback + ) + }, ) return true @@ -430,6 +463,8 @@ open class SoraStream : TmdbProvider() { val title: String? = null, val year: Int? = null, val orgTitle: String? = null, + val show: String? = null, + val airedYear: Int? = null, ) data class Data( @@ -525,6 +560,7 @@ open class SoraStream : TmdbProvider() { @JsonProperty("release_date") val releaseDate: String? = null, @JsonProperty("first_air_date") val firstAirDate: String? = null, @JsonProperty("overview") val overview: String? = null, + @JsonProperty("original_language") val original_language: String? = null, @JsonProperty("status") val status: String? = null, @JsonProperty("genres") val genres: ArrayList? = arrayListOf(), @JsonProperty("seasons") val seasons: ArrayList? = arrayListOf(),