diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index 579f711d..be879fef 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 23 +version = 24 cloudstream { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index b97926ab..59cb06e8 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -153,7 +153,7 @@ object SoraExtractor : SoraStream() { callback: (ExtractorLink) -> Unit ) { - var iframeDbgo: String? = null + val iframeDbgo: String? val script = if (season == null) { val doc = app.get("$dbgoAPI/imdb.php?id=$id").document iframeDbgo = doc.select("div.myvideo iframe").attr("src") @@ -181,10 +181,11 @@ object SoraExtractor : SoraStream() { 1 ) + val ref = getBaseUrl(iframeDbgo) decryptStreamUrl(source.toString()).split(",").map { links -> val quality = Regex("\\[([0-9]*p.*?)]").find(links)?.groupValues?.getOrNull(1).toString().trim() - links.replace("[$quality]", "").split("or").map { it.trim() } + links.replace("[$quality]", "").split(" or ").map { it.trim() } .map { link -> val name = if (link.contains(".m3u8")) "Dbgo (Main)" else "Dbgo (Backup)" callback.invoke( @@ -192,11 +193,11 @@ object SoraExtractor : SoraStream() { name, name, link, - "${getBaseUrl(iframeDbgo)}/", + "$ref/", getQuality(quality), isM3u8 = link.contains(".m3u8"), headers = mapOf( - "Origin" to getBaseUrl(iframeDbgo) + "Origin" to ref ) ) ) @@ -715,6 +716,46 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeKimcartoon( + title: String? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val fixTitle = title.fixKimTitle() + val url = if(season == null) { + "$kimcartoonAPI/Cartoon/$fixTitle" + } else { + "$kimcartoonAPI/Cartoon/$fixTitle-season-$season" + } + + val doc = app.get(url).document + val iframe = if (season == null) { + doc.select("table.listing tr td a").firstNotNullOf { it.attr("href") } + } else { + doc.select("table.listing tr td a").map { + it.attr("href") + }.first { it.contains("Season-$season", true) && it.contains("Episode-$episode", true) } + } ?: return + + val source = app.get(fixUrl(iframe, kimcartoonAPI)).document.select("div#divContentVideo iframe").attr("src") + loadExtractor(source, "$kimcartoonAPI/", subtitleCallback) { link -> + callback.invoke( + ExtractorLink( + "Luxubu", + "Luxubu", + link.url, + link.referer, + link.quality, + link.isM3u8, + link.headers, + link.extractorData + ) + ) + } + } + } data class FilmxyCookies( @@ -771,6 +812,10 @@ private fun String?.fixTitle(): String? { return this?.replace(":", "")?.replace(" ", "-")?.lowercase()?.replace("-–-", "-") } +private fun String?.fixKimTitle(): String? { + return this?.replace(Regex("[!%:]|( &)"), "")?.replace(" ", "-")?.lowercase()?.replace("-–-", "-") +} + fun getLanguage(str: String): String { return if (str.contains("(in_ID)")) "Indonesian" else str } @@ -847,6 +892,25 @@ suspend fun loadLinksWithWebView( ) } +fun fixUrl(url: String, domain: 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 domain + url + } + return "$domain/$url" + } +} + data class HdMovieBoxSource( @JsonProperty("videoUrl") val videoUrl: String? = null, @JsonProperty("videoServer") val videoServer: String? = null, diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt index 5d264272..bac6d0bd 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -9,6 +9,7 @@ import com.hexated.SoraExtractor.invokeFilmxy import com.hexated.SoraExtractor.invokeGogo import com.hexated.SoraExtractor.invokeHDMovieBox import com.hexated.SoraExtractor.invokeIdlix +import com.hexated.SoraExtractor.invokeKimcartoon import com.hexated.SoraExtractor.invokeLocalSources import com.hexated.SoraExtractor.invokeMovieHab import com.hexated.SoraExtractor.invokeNoverse @@ -61,6 +62,7 @@ open class SoraStream : TmdbProvider() { const val olgplyAPI = "https://olgply.xyz" const val uniqueStreamAPI = "https://uniquestream.net" const val filmxyAPI = "https://www.filmxy.vip" + const val kimcartoonAPI = "https://kimcartoon.li" fun getType(t: String?): TvType { return when (t) { @@ -332,46 +334,46 @@ open class SoraStream : TmdbProvider() { } argamap( - { - invokeSoraVIP( - res.title, - res.orgTitle, - res.year, - res.season, - res.episode, - subtitleCallback, - callback - ) - }, - { - val json = app.get( - query, - referer = referer, - headers = mapOf("User-Agent" to getRandomUserAgent()) - ).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( - getLanguage(sub.lang.toString()), - sub.url ?: return@map null - ) - ) - } - }, +// { +// invokeSoraVIP( +// res.title, +// res.orgTitle, +// res.year, +// res.season, +// res.episode, +// subtitleCallback, +// callback +// ) +// }, +// { +// val json = app.get( +// query, +// referer = referer, +// headers = mapOf("User-Agent" to getRandomUserAgent()) +// ).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( +// getLanguage(sub.lang.toString()), +// sub.url ?: return@map null +// ) +// ) +// } +// }, { invokeTwoEmbed(res.id, res.season, res.episode, subtitleCallback, callback) }, @@ -445,6 +447,9 @@ open class SoraStream : TmdbProvider() { { invokeFilmxy(res.imdbId, res.season, res.episode, subtitleCallback, callback) }, + { + invokeKimcartoon(res.title, res.season, res.episode, subtitleCallback, callback) + }, ) return true