From a91cfbcf661e454683d4d15e0e21dc6c41602409 Mon Sep 17 00:00:00 2001 From: hexated Date: Mon, 30 Jan 2023 10:42:55 +0700 Subject: [PATCH] [Sora] added Chillmovies --- .../main/kotlin/com/hexated/SoraExtractor.kt | 92 ++++++++++--------- .../src/main/kotlin/com/hexated/SoraStream.kt | 11 +++ .../src/main/kotlin/com/hexated/SoraUtils.kt | 71 ++++++++++++++ 3 files changed, 133 insertions(+), 41 deletions(-) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt index 354a6663..a8a1c1a7 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraExtractor.kt @@ -2088,42 +2088,9 @@ object SoraExtractor : SoraStream() { "Origin" to baymovies, "cf_cache_token" to "UKsVpQqBMxB56gBfhYKbfCVkRIXMh42pk6G4DdkXXoVh7j4BjV" ) - - val dotSlug = title.fixTitle()?.replace("-", ".") ?: return - val spaceSlug = title.fixTitle()?.replace("-", " ") ?: return - val (episodeSlug, seasonSlug) = if (season == null) { - listOf("", "") - } else { - listOf( - if (episode!! < 10) "0$episode" else episode, - if (season < 10) "0$season" else season - ) - } - - val query = if (season == null) { - "$title $year" - } else { - "$title S${seasonSlug}E${episodeSlug}" - } - - val media = - app.get("$baymoviesAPI//0:search?q=$query&page_token=&page_index=0", headers = headers) - .parsedSafe()?.data?.files?.filter { media -> - (if (season == null) { - media.name?.contains("$year") == true - } else { - media.name?.contains(Regex("(?i)S${seasonSlug}.?E${episodeSlug}")) == true - }) && media.name?.contains( - "720p", - true - ) == false && (media.mimeType == "video/x-matroska" || media.mimeType == "video/mp4") && (media.name.replace( - "-", - "." - ).contains( - dotSlug, - true - ) || media.name.contains(spaceSlug, true)) - }?.distinctBy { it.name } ?: return + val query = getIndexQuery(title, year, season, episode) + val search = app.get("$baymoviesAPI//0:search?q=$query&page_token=&page_index=0", headers = headers) + val media = searchIndex(title, season, episode, year, search) ?: return media.apmap { file -> val expiry = (System.currentTimeMillis() + 345600000).toString() @@ -2161,6 +2128,49 @@ object SoraExtractor : SoraStream() { } + suspend fun invokeChillmovies( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null, + callback: (ExtractorLink) -> Unit, + ) { + val query = getIndexQuery(title, year, season, episode) + val body = + """{"q":"$query","password":null,"page_token":null,"page_index":0}""".toRequestBody( + RequestBodyTypes.JSON.toMediaTypeOrNull() + ) + val search = app.post("$chillmoviesAPI/0:search", requestBody = body) + val media = searchIndex(title, season, episode, year, search) ?: return + media.apmap { file -> + val pathBody = """{"id":"${file.id ?: return@apmap null}"}""".toRequestBody( + RequestBodyTypes.JSON.toMediaTypeOrNull() + ) + val path = app.post("$chillmoviesAPI/0:id2path", requestBody = pathBody).text.let { + fixUrl(it, "$chillmoviesAPI/0:") + }.encodeUrl() + val size = file.size?.toDouble() ?: return@apmap null + val sizeFile = "%.2f GB".format(bytesToGigaBytes(size)) + val quality = + Regex("(\\d{3,4})[pP]").find( + file.name ?: return@apmap null + )?.groupValues?.getOrNull(1)?.toIntOrNull() + ?: Qualities.P1080.value + + callback.invoke( + ExtractorLink( + "Chillmovies [$sizeFile]", + "Chillmovies [$sizeFile]", + path, + "", + quality, + ) + ) + + } + + } + } class StreamM4u : XStreamCdn() { @@ -2471,7 +2481,7 @@ data class WatchsomuchSubResponses( @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), ) -data class Baymovies( +data class IndexMedia( @JsonProperty("id") val id: String? = null, @JsonProperty("driveId") val driveId: String? = null, @JsonProperty("mimeType") val mimeType: String? = null, @@ -2480,10 +2490,10 @@ data class Baymovies( @JsonProperty("modifiedTime") val modifiedTime: String? = null, ) -data class BaymoviesData( - @JsonProperty("files") val files: ArrayList? = arrayListOf(), +data class IndexData( + @JsonProperty("files") val files: ArrayList? = arrayListOf(), ) -data class BaymoviesSearch( - @JsonProperty("data") val data: BaymoviesData? = null, +data class IndexSearch( + @JsonProperty("data") val data: IndexData? = 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 ee99741a..812ce740 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraStream.kt @@ -5,6 +5,7 @@ import com.hexated.SoraExtractor.invoke123Movie import com.hexated.SoraExtractor.invokeAnimes import com.hexated.SoraExtractor.invokeBaymovies import com.hexated.SoraExtractor.invokeBollyMaza +import com.hexated.SoraExtractor.invokeChillmovies import com.hexated.SoraExtractor.invokeDbgo import com.hexated.SoraExtractor.invokeFilmxy import com.hexated.SoraExtractor.invokeFlixhq @@ -106,6 +107,7 @@ open class SoraStream : TmdbProvider() { const val movie123NetAPI = "https://ww7.0123movie.net" const val smashyStreamAPI = "https://embed.smashystream.com" const val baymoviesAPI = "https://thebayindexpublicgroupapi.zindex.eu.org" + const val chillmoviesAPI = "https://chill.aicirou.workers.dev" fun getType(t: String?): TvType { return when (t) { @@ -539,6 +541,15 @@ open class SoraStream : TmdbProvider() { res.episode, callback ) + }, + { + invokeChillmovies ( + res.title, + res.year, + res.season, + res.episode, + callback + ) } ) diff --git a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt index 0f8fc705..9d245fcd 100644 --- a/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt +++ b/SoraStream/src/main/kotlin/com/hexated/SoraUtils.kt @@ -21,6 +21,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import org.jsoup.nodes.Document import java.net.URI +import java.net.URL data class FilmxyCookies( val phpsessid: String? = null, @@ -598,6 +599,70 @@ fun List>?.matchingEpisode(episode: Int?): String? { }?.get("id") } +fun getEpisodeSlug( + season: Int? = null, + episode: Int? = null, +): Pair { + return if (season == null && episode == null) { + "" to "" + } else { + (if (season!! < 10) "0$season" else "$season") to (if (episode!! < 10) "0$episode" else "$episode") + } +} + +fun getTitleSlug(title: String? = null): Pair { + return title.fixTitle()?.replace("-", ".") to title.fixTitle()?.replace("-", " ") +} + +fun getIndexQuery( + title: String? = null, + year: Int? = null, + season: Int? = null, + episode: Int? = null +): String { + val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) + return if (season == null) { + "$title $year" + } else { + "$title S${seasonSlug}E${episodeSlug}" + } +} + +fun searchIndex( + title: String? = null, + season: Int? = null, + episode: Int? = null, + year: Int? = null, + response: NiceResponse +): List? { + val (dotSlug, spaceSlug) = getTitleSlug(title) + val (seasonSlug, episodeSlug) = getEpisodeSlug(season, episode) + val mimeType = arrayOf( + "video/x-matroska", + "video/mp4", + "video/x-msvideo" + ) + return response.parsedSafe()?.data?.files?.filter { media -> + (if (season == null) { + media.name?.contains("$year") == true + } else { + media.name?.contains(Regex("(?i)S${seasonSlug}.?E${episodeSlug}")) == true + }) && media.name?.contains( + "720p", + true + ) == false && (media.mimeType in mimeType) && (media.name.replace( + "-", + "." + ).contains( + "$dotSlug", + true + ) || media.name.replace( + "-", + " " + ).contains("$spaceSlug", true)) + }?.distinctBy { it.name } +} + suspend fun getConfig(): BaymoviesConfig { val regex = """const country = "(.*?)"; const downloadtime = "(.*?)"; @@ -687,6 +752,12 @@ fun getDbgoLanguage(str: String): String { } } +fun String.encodeUrl() : String { + val url = URL(this) + val uri = URI(url.protocol, url.userInfo, url.host, url.port, url.path, url.query, url.ref) + return uri.toURL().toString() +} + fun getBaseUrl(url: String): String { return URI(url).let { "${it.scheme}://${it.host}"