From 211c58b0ef493167d0bc34f7faf6e3ea68bf3197 Mon Sep 17 00:00:00 2001 From: lisa Date: Sat, 23 Dec 2023 09:16:09 +0700 Subject: [PATCH] sora: fix m4uhd --- .github/workflows/build.yml | 2 - LayarKacaProvider/build.gradle.kts | 2 +- .../kotlin/com/hexated/LayarKacaProvider.kt | 3 +- SoraStream/build.gradle.kts | 3 +- .../src/main/kotlin/com/hexated/Extractors.kt | 60 ++++----- .../main/kotlin/com/hexated/SoraExtractor.kt | 121 ++++++++---------- .../src/main/kotlin/com/hexated/SoraParser.kt | 35 ++--- .../src/main/kotlin/com/hexated/SoraStream.kt | 9 +- .../main/kotlin/com/hexated/SoraStreamLite.kt | 6 +- 9 files changed, 103 insertions(+), 138 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c88973ef..b5e17d0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,6 @@ jobs: SFMOVIES_API: ${{ secrets.SFMOVIES_API }} CINEMATV_API: ${{ secrets.CINEMATV_API }} OMOVIES_API: ${{ secrets.OMOVIES_API }} - FEBBOX_API: ${{ secrets.FEBBOX_API }} run: | cd $GITHUB_WORKSPACE/src echo TMDB_API=$TMDB_API >> local.properties @@ -71,7 +70,6 @@ jobs: echo SFMOVIES_API=$SFMOVIES_API >> local.properties echo CINEMATV_API=$CINEMATV_API >> local.properties echo OMOVIES_API=$OMOVIES_API >> local.properties - echo FEBBOX_API=$FEBBOX_API >> local.properties - name: Build Plugins run: | diff --git a/LayarKacaProvider/build.gradle.kts b/LayarKacaProvider/build.gradle.kts index 5f978538..85bf1cb9 100644 --- a/LayarKacaProvider/build.gradle.kts +++ b/LayarKacaProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 17 +version = 18 cloudstream { diff --git a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt index 34ebfdc7..552e412e 100644 --- a/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt +++ b/LayarKacaProvider/src/main/kotlin/com/hexated/LayarKacaProvider.kt @@ -9,7 +9,7 @@ import org.jsoup.nodes.Element class LayarKacaProvider : MainAPI() { override var mainUrl = "https://tv6.lk21official.wiki" - private var seriesUrl = "https://tv8.nontondrama.click/" + private var seriesUrl = "https://tv8.nontondrama.click" override var name = "LayarKaca" override val hasMainPage = true override var lang = "id" @@ -40,6 +40,7 @@ class LayarKacaProvider : MainAPI() { } private suspend fun getProperLink(url: String): String? { + if(url.startsWith(seriesUrl)) return url val res = app.get(url).document return if (res.select("title").text().contains("- Nontondrama", true)) { res.selectFirst("div#content a")?.attr("href") diff --git a/SoraStream/build.gradle.kts b/SoraStream/build.gradle.kts index cd61e696..327754b2 100644 --- a/SoraStream/build.gradle.kts +++ b/SoraStream/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.konan.properties.Properties // use an integer for version numbers -version = 207 +version = 208 android { defaultConfig { @@ -9,7 +9,6 @@ android { properties.load(project.rootProject.file("local.properties").inputStream()) buildConfigField("String", "TMDB_API", "\"${properties.getProperty("TMDB_API")}\"") - buildConfigField("String", "FEBBOX_API", "\"${properties.getProperty("FEBBOX_API")}\"") buildConfigField("String", "OMOVIES_API", "\"${properties.getProperty("OMOVIES_API")}\"") buildConfigField("String", "CINEMATV_API", "\"${properties.getProperty("CINEMATV_API")}\"") buildConfigField("String", "SFMOVIES_API", "\"${properties.getProperty("SFMOVIES_API")}\"") diff --git a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt index 0d72837f..0e5ae558 100644 --- a/SoraStream/src/main/kotlin/com/hexated/Extractors.kt +++ b/SoraStream/src/main/kotlin/com/hexated/Extractors.kt @@ -124,8 +124,8 @@ open class Playm4u : ExtractorApi() { return "$this\\s*=\\s*[\"'](\\S+)[\"'];".toRegex().find(data)?.groupValues?.get(1) ?: "" } - private fun String.toLanguage() : String { - return if(this == "EN") "English" else this + private fun String.toLanguage(): String { + return if (this == "EN") "English" else this } data class Source( @@ -209,7 +209,10 @@ open class VCloud : ExtractorApi() { ) ).document.select("p.text-success ~ a").apmap { val link = it.attr("href") - if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains("/dl.php?")) { + if (link.contains("workers.dev") || it.text().contains("[Server : 1]") || link.contains( + "/dl.php?" + ) + ) { callback.invoke( ExtractorLink( this.name, @@ -221,7 +224,7 @@ open class VCloud : ExtractorApi() { ) ) } else { - val direct = if(link.contains("gofile.io")) app.get(link).url else link + val direct = if (link.contains("gofile.io")) app.get(link).url else link loadExtractor(direct, referer, subtitleCallback, callback) } } @@ -247,12 +250,14 @@ open class Streamruby : ExtractorApi() { callback: (ExtractorLink) -> Unit ) { val id = "/e/(\\w+)".toRegex().find(url)?.groupValues?.get(1) ?: return - val response = app.post("$mainUrl/dl", data = mapOf( - "op" to "embed", - "file_code" to id, - "auto" to "1", - "referer" to "", - ), referer = referer) + val response = app.post( + "$mainUrl/dl", data = mapOf( + "op" to "embed", + "file_code" to id, + "auto" to "1", + "referer" to "", + ), referer = referer + ) val script = if (!getPacked(response.text).isNullOrEmpty()) { getAndUnpack(response.text) } else { @@ -282,17 +287,25 @@ open class Uploadever : ExtractorApi() { ) { var res = app.get(url, referer = referer).document val formUrl = res.select("form").attr("action") - var formData = res.select("form input").associate { it.attr("name") to it.attr("value") }.filterKeys { it != "go" } + var formData = res.select("form input").associate { it.attr("name") to it.attr("value") } + .filterKeys { it != "go" } .toMutableMap() val formReq = app.post(formUrl, data = formData) res = formReq.document - val captchaKey = res.select("script[src*=https://www.google.com/recaptcha/api.js?render=]").attr("src").substringAfter("render=") + val captchaKey = + res.select("script[src*=https://www.google.com/recaptcha/api.js?render=]").attr("src") + .substringAfter("render=") val token = getCaptchaToken(url, captchaKey, referer = "$mainUrl/") - formData = res.select("form#down input").associate { it.attr("name") to it.attr("value") }.toMutableMap() + formData = res.select("form#down input").associate { it.attr("name") to it.attr("value") } + .toMutableMap() formData["adblock_detected"] = "0" formData["referer"] = url - res = app.post(formReq.url, data = formData + mapOf("g-recaptcha-response" to "$token"), cookies = formReq.cookies).document + res = app.post( + formReq.url, + data = formData + mapOf("g-recaptcha-response" to "$token"), + cookies = formReq.cookies + ).document val video = res.select("div.download-button a.btn.btn-dow.recaptchav2").attr("href") callback.invoke( @@ -325,18 +338,7 @@ open class Netembed : ExtractorApi() { val script = getAndUnpack(response.text) val m3u8 = Regex("((https:|http:)//.*\\.m3u8)").find(script)?.groupValues?.getOrNull(1) ?: return - if(m3u8.startsWith("https://www.febbox.com")) { - callback.invoke( - ExtractorLink( - this.name, - this.name, - m3u8, - "$mainUrl/", - getQuality(m3u8), - INFER_TYPE - ) - ) - } else { + if (!m3u8.startsWith("https://www.febbox.com")) { M3u8Helper.generateM3u8( this.name, m3u8, @@ -344,12 +346,6 @@ open class Netembed : ExtractorApi() { ).forEach(callback) } } - - private suspend fun getQuality(url: String) : Int { - val res = app.get(url, referer = "$mainUrl/").text - val regex = "#quality:\\s*(\\S+)".toRegex().find(res)?.groupValues?.get(1) - return getQualityFromName(regex) - } } class Streamwish : Filesim() { diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 7fe1c9bc..8c4853af 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -1354,14 +1354,14 @@ object SoraExtractor : SoraStream() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ) { + val slugTitle = title.createSlug() val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) - val req = app.get("$m4uhdAPI/search/${title.createSlug()}.html") + val req = app.get("$m4uhdAPI/search/$slugTitle.html") val referer = getBaseUrl(req.url) val scriptData = req.document.select("div.row div.item").map { ele -> Triple( - ele.select("div.tiptitle p").text(), - ele.select("div.jtip-top div:last-child").text().substringBefore("–") - .filter { it.isDigit() }, + ele.select("div.tiptitle p").text().substringBefore("(").trim().createSlug(), + ele.select("div.jtip-top div:last-child").text().filter { it.isDigit() }, ele.selectFirst("a")?.attr("href") ) } @@ -1370,7 +1370,7 @@ object SoraExtractor : SoraStream() { scriptData.firstOrNull() } else { scriptData.find { - it.first.contains(Regex("(?i)$title \\($year\\s?\\)")) && if (season != null) it.third?.contains( + it.first.equals(slugTitle) && it.second == "$year" && if (season != null) it.third?.contains( "-tvshow-" ) == true else it.third?.contains("-movie-") == true } @@ -2133,52 +2133,6 @@ object SoraExtractor : SoraStream() { } - suspend fun invokeBlackvid( - tmdbId: Int? = null, - season: Int? = null, - episode: Int? = null, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit, - ) { - val key = "b6055c533c19131a638c3d2299d525d5ec08a814" - val url = if (season == null) { - "$blackvidAPI/v3/movie/sources/$tmdbId?key=$key" - } else { - "$blackvidAPI/v3/tv/sources/$tmdbId/$season/$episode?key=$key" - } - - val res = request(url).peekBody(1024 * 512) - val bytes = res.bytes().also { res.closeQuietly() } - val data = bytes.decrypt("2378f8e4e844f2dc839ab48f66e00acc2305a401") - val json = tryParseJson(data) - - json?.sources?.map { source -> - source.sources.map s@{ s -> - callback.invoke( - ExtractorLink( - "Blackvid", - "Blackvid${source.label}", - s.url ?: return@s, - "https://blackvid.space/", - if (s.quality.equals("4k")) Qualities.P2160.value else s.quality?.toIntOrNull() - ?: Qualities.P1080.value, - INFER_TYPE - ) - ) - } - } - - json?.subtitles?.map { sub -> - subtitleCallback.invoke( - SubtitleFile( - sub.language.takeIf { it?.isNotEmpty() == true } ?: return@map, - sub.url ?: return@map, - ) - ) - } - - } - suspend fun invokeShowflix( title: String? = null, year: Int? = null, @@ -2312,31 +2266,62 @@ object SoraExtractor : SoraStream() { } suspend fun invokeFebbox( - imdbId: String? = null, + title: String? = null, + year: Int? = null, season: Int? = null, episode: Int? = null, callback: (ExtractorLink) -> Unit, ) { - val url = if (season == null) { - "$febboxAPI/stream/movie/$imdbId.json" + val showboxApi = "https://www.showbox.media" + val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) + + val res = app.post( + "$showboxApi/search/autocomplate2", data = mapOf( + "keyword" to "$title" + ), headers = mapOf("X-Requested-With" to "XMLHttpRequest") + ).parsed().let { Jsoup.parse(it) } + + val mediaId = res.select("a.nav-item").find { + it.select("h3.film-name").text() + .equals(title, true) && it.select("div.film-infor > span:first-child").text() + .contains(if (season == null) "$year" else "SS") && it.select("div.film-infor > span:last-child") + .text() + .equals(if (season == null) "Movie" else "TV") + }?.attr("href")?.substringAfterLast("/") + + val shareKey = + app.get("$showboxApi/index/share_link?id=${mediaId ?: return}&type=${if (season == null) "1" else "2"}") + .parsedSafe()?.data?.link?.substringAfterLast("/") + + val headers = mapOf("Accept-Language" to "en") + val shareRes = app.get("$febboxAPI/file/file_share_list?share_key=${shareKey ?: return}", headers = headers) + .parsedSafe()?.data + + val fids = if (season == null) { + shareRes?.file_list } else { - "$febboxAPI/stream/series/$imdbId:$season:$episode.json" + val parentId = shareRes?.file_list?.find { it.file_name.equals("season $season", true) }?.fid + app.get("$febboxAPI/file/file_share_list?share_key=${shareKey}&parent_id=$parentId&page=1", headers = headers) + .parsedSafe()?.data?.file_list?.filter { + it.file_name?.contains( + "s${seasonSlug}e${episodeSlug}", + true + ) == true + } } - val res = request(url).body - val data = res.string().also { res.closeQuietly() } - val video = tryParseJson(data)?.streams?.find { it.url?.startsWith("https://www.febbox.com") == true }?.url - - callback.invoke( - ExtractorLink( - "Febbox", - "Febbox", - video ?: return, - "", - Qualities.P1080.value, - INFER_TYPE + fids?.mapIndexed { index, fileList -> + callback.invoke( + ExtractorLink( + "Febbox", + "Febbox [Server $index]", + "$febboxAPI/hls/main/${fileList.oss_fid}.m3u8", + "", + getIndexQuality(fileList.file_name), + isM3u8 = true + ) ) - ) + } } diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt index 40a19102..01ebad4a 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraParser.kt @@ -334,26 +334,6 @@ data class EMovieTraks( @JsonProperty("label") val label: String? = null, ) -data class BlackvidSubtitles( - @JsonProperty("language") val language: String? = null, - @JsonProperty("url") val url: String? = null, -) - -data class BlackvidSource( - @JsonProperty("quality") var quality: String? = null, - @JsonProperty("url") var url: String? = null, -) - -data class BlackvidSources( - @JsonProperty("label") var label: String? = null, - @JsonProperty("sources") var sources: ArrayList = arrayListOf() -) - -data class BlackvidResponses( - @JsonProperty("sources") var sources: ArrayList = arrayListOf(), - @JsonProperty("subtitles") var subtitles: ArrayList = arrayListOf() -) - data class ShowflixResultsMovies( @JsonProperty("movieName") val movieName: String? = null, @JsonProperty("streamwish") val streamwish: String? = null, @@ -464,9 +444,16 @@ data class AoneroomResponse( } data class FebboxResponse( - @JsonProperty("streams") val streams: ArrayList? = arrayListOf(), + @JsonProperty("data") val data: Data? = null, ) { - data class Streams( - @JsonProperty("url") val url: String? = null, - ) + data class Data( + @JsonProperty("link") val link: String? = null, + @JsonProperty("file_list") val file_list: ArrayList? = arrayListOf(), + ) { + data class FileList( + @JsonProperty("fid") val fid: Long? = null, + @JsonProperty("file_name") val file_name: String? = null, + @JsonProperty("oss_fid") val oss_fid: Long? = null, + ) + } } \ 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 index 76acfb44..0477026d 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.hexated.SoraExtractor.invoke2embed import com.hexated.SoraExtractor.invokeAnimes import com.hexated.SoraExtractor.invokeAoneroom -import com.hexated.SoraExtractor.invokeBlackvid import com.hexated.SoraExtractor.invokeBollyMaza import com.hexated.SoraExtractor.invokeDbgo import com.hexated.SoraExtractor.invokeFilmxy @@ -113,10 +112,9 @@ open class SoraStream : TmdbProvider() { const val dramadayAPI = "https://dramaday.me" const val animetoshoAPI = "https://animetosho.org" const val watchflxAPI = "https://watchflx.tv" - const val blackvidAPI = "https://prod.api.blackvid.space" const val showflixAPI = "https://showflix.space" const val aoneroomAPI = "https://api3.aoneroom.com" - const val febboxAPI = BuildConfig.FEBBOX_API + const val febboxAPI = "https://www.febbox.com" const val fdMoviesAPI = "https://freedrivemovie.lol" const val uhdmoviesAPI = "https://uhdmovies.zip" @@ -364,8 +362,9 @@ open class SoraStream : TmdbProvider() { argamap( { - if (!res.isAnime) invokeFebbox( - res.imdbId, + invokeFebbox( + res.title, + res.year, res.season, res.episode, callback diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt index ce8701cf..5e5afe20 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStreamLite.kt @@ -3,7 +3,6 @@ package com.hexated import com.hexated.SoraExtractor.invoke2embed import com.hexated.SoraExtractor.invokeAnimes import com.hexated.SoraExtractor.invokeAoneroom -import com.hexated.SoraExtractor.invokeBlackvid import com.hexated.SoraExtractor.invokeDbgo import com.hexated.SoraExtractor.invokeDoomovies import com.hexated.SoraExtractor.invokeDramaday @@ -54,8 +53,9 @@ class SoraStreamLite : SoraStream() { argamap( { - if (!res.isAnime) invokeFebbox( - res.imdbId, + invokeFebbox( + res.title, + res.year, res.season, res.episode, callback