diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index a16b517e..e9a2662c 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 18 +version = 19 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/Jeniusplay.kt b/SoraStream/src/main/kotlin/com/hexated/Jeniusplay.kt deleted file mode 100644 index 7df5369c..00000000 --- a/SoraStream/src/main/kotlin/com/hexated/Jeniusplay.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.hexated - -import com.fasterxml.jackson.annotation.JsonProperty -import com.lagradost.cloudstream3.SubtitleFile -import com.lagradost.cloudstream3.app -import com.lagradost.cloudstream3.utils.* -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson - -class Jeniusplay : ExtractorApi() { - override val name = "Jeniusplay" - override val mainUrl = "https://jeniusplay.com" - override val requiresReferer = true - - override suspend fun getUrl( - url: String, - referer: String?, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ) { - val document = app.get(url, referer = "$mainUrl/").document - val hash = url.split("/").last().substringAfter("data=") - - val m3uLink = app.post( - url = "$mainUrl/player/index.php?data=$hash&do=getVideo", - data = mapOf("hash" to hash, "r" to "$referer"), - referer = url, - headers = mapOf("X-Requested-With" to "XMLHttpRequest") - ).parsed().videoSource - - M3u8Helper.generateM3u8( - this.name, - m3uLink, - url, - ).forEach(callback) - - - document.select("script").map { script -> - if (script.data().contains("eval(function(p,a,c,k,e,d)")) { - val subData = - getAndUnpack(script.data()).substringAfter("\"tracks\":[").substringBefore("],") - tryParseJson>("[$subData]")?.map { subtitle -> - subtitleCallback.invoke( - SubtitleFile( - getLanguage(subtitle.label.toString()), - subtitle.file - ) - ) - } - } - } - } - - private fun getLanguage(str: String): String { - return when { - str.lowercase().contains("indonesia") || str.lowercase() - .contains("bahasa") -> "Indonesian" - else -> str - } - } - - data class ResponseSource( - @JsonProperty("hls") val hls: Boolean, - @JsonProperty("videoSource") val videoSource: String, - @JsonProperty("securedLink") val securedLink: String?, - ) - - data class Tracks( - @JsonProperty("kind") val kind: String?, - @JsonProperty("file") val file: String, - @JsonProperty("label") val label: String?, - ) -} \ No newline at end of file diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 91ea7dd3..e6f83ed7 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -526,6 +526,71 @@ object SoraExtractor : SoraStream() { } } + suspend fun invokeUniqueStream( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val fixTitle = title.fixTitle() + val url = if (season == null) { + "$uniqueStreamAPI/movies/$fixTitle-$year" + } else { + "$uniqueStreamAPI/episodes/$fixTitle-season-$season-episode-$episode" + } + + val document = app.get(url).document + val type = if (url.contains("/movie/")) "movie" else "tv" + document.select("ul#playeroptionsul > li").apmap { el -> + val id = el.attr("data-post") + val nume = el.attr("data-nume") + val source = app.post( + url = "$uniqueStreamAPI/wp-admin/admin-ajax.php", + data = mapOf( + "action" to "doo_player_ajax", + "post" to id, + "nume" to nume, + "type" to type + ), + headers = mapOf("X-Requested-With" to "XMLHttpRequest"), + referer = url + ).parsed().embed_url.let { fixUrl(it) } + + if (source.contains("uniquestream")) { + val resDoc = app.get( + source, referer = "$uniqueStreamAPI/", headers = mapOf( + "Accept" to "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8" + ) + ).document + val srcm3u8 = resDoc.selectFirst("script:containsData(let url =)")?.data()?.let { + Regex("['|\"](.*?.m3u8)['|\"]").find(it)?.groupValues?.getOrNull(1) + } ?: return@apmap null + val quality = app.get(srcm3u8, referer = source, headers = mapOf( + "Accept" to "*/*", + )).text.let { quality -> + if(quality.contains("RESOLUTION=1920")) Qualities.P1080.value else Qualities.P720.value + } + callback.invoke( + ExtractorLink( + "UniqueStream", + "UniqueStream", + srcm3u8, + source, + quality, + true, + headers = mapOf( + "Accept" to "*/*", + ) + ) + ) + } else { + loadExtractor(source, "$uniqueStreamAPI/", subtitleCallback, callback) + } + } + } + suspend fun invokeNoverse( title: String? = null, season: Int? = null, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 487e7b5b..89cf8f60 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -15,6 +15,7 @@ import com.hexated.SoraExtractor.invokeOlgply import com.hexated.SoraExtractor.invokeSeries9 import com.hexated.SoraExtractor.invokeSoraVIP import com.hexated.SoraExtractor.invokeTwoEmbed +import com.hexated.SoraExtractor.invokeUniqueStream import com.hexated.SoraExtractor.invokeVidSrc import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId @@ -57,6 +58,7 @@ open class SoraStream : TmdbProvider() { const val idlixAPI = "https://109.234.36.69" const val noverseAPI = "https://www.nollyverse.com" const val olgplyAPI = "https://olgply.xyz" + const val uniqueStreamAPI = "https://uniquestream.net" fun getType(t: String?): TvType { return when (t) { @@ -144,14 +146,13 @@ open class SoraStream : TmdbProvider() { referer = "$mainAPI/" ).parsedSafe()?.results?.mapNotNull { media -> media.toSearchResponse() - } ?: throw ErrorLoadingException("Invalid Json reponse") - searchResponse.addAll(mainResponse) + } + if(mainResponse?.isNotEmpty() == true) searchResponse.addAll(mainResponse) val animeResponse = app.get("$mainServerAPI/search/anime/$query?_data=routes/search/anime/\$animeKeyword") .parsedSafe()?.searchResults?.results?.mapNotNull { anime -> anime.toSearchResponse() } - ?: throw ErrorLoadingException("Invalid Json reponse") - searchResponse.addAll(animeResponse) + if(animeResponse?.isNotEmpty() == true) searchResponse.addAll(animeResponse) return searchResponse } @@ -388,6 +389,9 @@ open class SoraStream : TmdbProvider() { }, { invokeNoverse(res.title, res.season, res.episode, callback) + }, + { + invokeUniqueStream(res.title, res.year, res.season, res.episode, subtitleCallback, callback) } ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt index 805b7945..bdf4216e 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamPlugin.kt @@ -10,6 +10,5 @@ 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()) - registerExtractorAPI(Jeniusplay()) } } \ No newline at end of file