From 4fc0e3ce4b37792231993a4aec2bacb2ac7fa3f3 Mon Sep 17 00:00:00 2001 From: antonydp <38143733+antonydp@users.noreply.github.com> Date: Thu, 2 Feb 2023 00:31:01 +0100 Subject: [PATCH] Fixed StreamingCommunity Provider & some starlive fixes (#81) --- .../IlGenioDelloStreamingProvider.kt | 1 + StarLiveProvider/build.gradle.kts | 4 +- .../kotlin/com/lagradost/StarLiveProvider.kt | 8 +- StreamingcommunityProvider/build.gradle.kts | 4 +- .../lagradost/StreamingcommunityProvider.kt | 555 ++++++++---------- 5 files changed, 239 insertions(+), 333 deletions(-) diff --git a/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt b/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt index 728db9f..93be456 100644 --- a/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt +++ b/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt @@ -1,5 +1,6 @@ package com.lagradost +import android.util.Log import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addRating import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer diff --git a/StarLiveProvider/build.gradle.kts b/StarLiveProvider/build.gradle.kts index 1ccc2e7..566c604 100644 --- a/StarLiveProvider/build.gradle.kts +++ b/StarLiveProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 3 cloudstream { @@ -22,4 +22,4 @@ cloudstream { ) iconUrl = "https://www.google.com/s2/favicons?domain=starlive.xyz&sz=%size%" -} \ No newline at end of file +} diff --git a/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProvider.kt b/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProvider.kt index 5ee622d..374e4e6 100644 --- a/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProvider.kt +++ b/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProvider.kt @@ -36,7 +36,7 @@ class StarLiveProvider : MainAPI() { .map { LinkParser( fixUrl(it.selectFirst("a")?.attr("href")?:""), it.attr("class"), - it.selectFirst("span")?.text()?:"" + it.selectFirst("b")?.text()?:"" ) } val dayMatch = this.previousElementSiblings().toList().firstOrNull() { it.`is`("h3") }?.text() @@ -107,7 +107,7 @@ class StarLiveProvider : MainAPI() { ExtractorLink( source = this.name, name = data.name + " - " + data.language, - url = streamUrl, + url = fixUrl(streamUrl), quality = Qualities.Unknown.value, referer = referrerLink, isM3u8 = true @@ -123,7 +123,7 @@ class StarLiveProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - tryParseJson(data)?.linkData?.map { link -> + tryParseJson(data)?.linkData?.apmap { link -> extractVideoLinks(link, callback) } @@ -147,4 +147,4 @@ class StarLiveProvider : MainAPI() { @JsonProperty("matchData") val MatchData: MatchDataParser ) -} \ No newline at end of file +} diff --git a/StreamingcommunityProvider/build.gradle.kts b/StreamingcommunityProvider/build.gradle.kts index 61472f6..eb3acaa 100644 --- a/StreamingcommunityProvider/build.gradle.kts +++ b/StreamingcommunityProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 4 +version = 5 cloudstream { @@ -22,5 +22,5 @@ cloudstream { "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=streamingcommunity.best&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=streamingcommunity.online&sz=%size%" } diff --git a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt index 85dead3..eabf85d 100644 --- a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt +++ b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt @@ -7,9 +7,238 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.* import org.json.JSONObject -import java.net.URI import java.security.MessageDigest +class StreamingcommunityProvider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://streamingcommunity.cafe" + override var name = "StreamingCommunity" + override val hasMainPage = true + override val hasChromecastSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + ) + private val userAgent = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36" + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val webpage = app.get(mainUrl, headers = mapOf("user-agent" to userAgent)) + val document = webpage.document + mainUrl = webpage.url + val items = document.select("slider-title").subList(0, 3).map { + val films = it.attr("titles-json") + val videoData = parseJson>(films) + val searchResponses = videoData.subList(0, 12).apmap { searchr -> + searchr.toSearchResponse() + } + HomePageList(it.attr("slider-name"), searchResponses) + } + if (items.isEmpty()) throw ErrorLoadingException() + return HomePageResponse(items) + } + + override suspend fun search(query: String): List { + mainUrl = app.get(mainUrl, headers = mapOf("user-agent" to userAgent)).url + val queryFormatted = query.replace(" ", "%20") + val url = "$mainUrl/search?q=$queryFormatted" + val document = app.get(url, headers = mapOf("user-agent" to userAgent)).document + + val films = + document.selectFirst("the-search-page")!!.attr("records-json") + .replace(""", """"""") + + val searchResults = parseJson>(films) + return searchResults.apmap { result -> + result.toSearchResponse() + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url, headers = mapOf("user-agent" to userAgent)).document + val poster = Regex("url\\('(.*)'").find( + document.selectFirst("div.title-wrap")?.attributes() + ?.get("style") ?: "" + )?.groupValues?.lastOrNull() //posterMap[url] + val id = url.substringBefore("-").filter { it.isDigit() } + val datajs = app.post( + "$mainUrl/api/titles/preview/$id", + referer = mainUrl, + headers = mapOf("user-agent" to userAgent) + ).parsed() + + val type = if (datajs.type == "movie") { + TvType.Movie + } else { + TvType.TvSeries + } + + val trailerInfoJs = document.select("slider-trailer").attr("videos") + val trailerInfo = parseJson>(trailerInfoJs) + val trailerUrl = trailerInfo.firstOrNull()?.url?.let { code -> + "https://www.youtube.com/watch?v=$code" + } + + val year = datajs.releaseDate.substringBefore("-") + val correlates = document.selectFirst("slider-title")!!.attr("titles-json") + val correlatesData = parseJson>(correlates) + // Max size 15 to prevent network spam + val size = minOf(correlatesData.size, 15) + + val correlatesList = correlatesData.take(size).apmap { + it.toSearchResponse() + } + + if (type == TvType.TvSeries) { + val name = datajs.name + + val episodes = + Html.fromHtml(document.selectFirst("season-select")!!.attr("seasons")).toString() + val jsonEpisodes = parseJson>(episodes) + + val episodeList = jsonEpisodes.map { seasons -> + val season = seasons.number.toInt() + val sid = seasons.title_id + seasons.episodes.map { ep -> + val href = "$mainUrl/watch/$sid?e=${ep.id}" + val postImage = ep.images.firstOrNull()?.originalURL + + newEpisode(href) { + this.name = ep.name + this.season = season + this.episode = ep.number.toInt() + this.description = ep.plot + this.posterUrl = postImage + } + } + }.flatten() + + if (episodeList.isEmpty()) throw ErrorLoadingException("No Seasons Found") + + return newTvSeriesLoadResponse(name, url, type, episodeList) { + this.posterUrl = poster + this.year = year.filter { it.isDigit() }.toInt() + this.plot = document.selectFirst("div.plot-wrap > p")!!.text() + this.duration = datajs.runtime?.toInt() + this.rating = (datajs.votes[0].average.toFloatOrNull()?.times(1000))?.toInt() + this.tags = datajs.genres.map { it.name } + addTrailer(trailerUrl) + this.recommendations = correlatesList + } + } else { + return newMovieLoadResponse( + document.selectFirst("div > div > h1")!!.text(), + document.select("a.play-hitzone").attr("href"), + type, + document.select("a.play-hitzone").attr("href") + ) { + posterUrl = fixUrlNull(poster) + this.year = year.filter { it.isDigit() }.toInt() + this.plot = document.selectFirst("p.plot")!!.text() + this.rating = datajs.votes[0].average.toFloatOrNull()?.times(1000)?.toInt() + this.tags = datajs.genres.map { it.name } + this.duration = datajs.runtime?.toInt() + addTrailer(trailerUrl) + this.recommendations = correlatesList + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val ip = app.get("https://api.ipify.org/").text + val videosPage = app.get(data, headers = mapOf("user-agent" to userAgent)).document + val scwsidJs = videosPage.select("video-player").attr("response").replace(""", """"""") + val jsn = JSONObject(scwsidJs) + val scwsid = jsn.getString("scws_id") + val expire = (System.currentTimeMillis() / 1000 + 172800).toString() + + val token0 = "$expire$ip Yc8U6r8KjAKAepEA".toByteArray() + val token1 = MessageDigest.getInstance("MD5").digest(token0) + val token2 = base64Encode(token1) + val token = token2.replace("=", "").replace("+", "-").replace("/", "_") + + val link = "https://scws.work/master/$scwsid?token=$token&expires=$expire&n=1" + + callback.invoke( + ExtractorLink( + name, + name, + link, + isM3u8 = true, + referer = mainUrl, + quality = Qualities.Unknown.value + ) + ) + return true + } + + private suspend fun VideoElement.toSearchResponse(): MovieSearchResponse { + val id = this.id + val name = this.slug + val img = this.images[0].url + val number = translateNumber(this.images[0].serverID.toInt()) + val ip = translateIp(this.images[0].proxyID.toInt()) + val posterUrl = "https://$ip/images/$number/$img" + val videoUrl = "$mainUrl/titles/$id-$name" + //posterMap[videourl] = posterurl + val data = app.post( + "$mainUrl/api/titles/preview/$id", + referer = mainUrl, + headers = mapOf("user-agent" to userAgent) + ).text + val datajs = parseJson(data) + val type = if (datajs.type == "movie") { + TvType.Movie + } else { + TvType.TvSeries + } + + return newMovieSearchResponse(datajs.name, videoUrl, type) { + this.posterUrl = posterUrl + this.year = + datajs.releaseDate.substringBefore("-").filter { it.isDigit() }.toIntOrNull() + } + } + + private fun translateNumber(num: Int): Int? { + return when (num) { + 67 -> 1 + 71 -> 2 + 72 -> 3 + 73 -> 4 + 74 -> 5 + 75 -> 6 + 76 -> 7 + 77 -> 8 + 78 -> 9 + 79 -> 10 + 133 -> 11 + else -> null + } + } + + private fun translateIp(num: Int): String? { + return when (num) { + 16 -> "sc-b1-01.scws-content.net" + 17 -> "sc-b1-02.scws-content.net" + 18 -> "sc-b1-03.scws-content.net" + 85 -> "sc-b1-04.scws-content.net" + 95 -> "sc-b1-05.scws-content.net" + 117 -> "sc-b1-06.scws-content.net" + 141 -> "sc-b1-07.scws-content.net" + 142 -> "sc-b1-08.scws-content.net" + 143 -> "sc-b1-09.scws-content.net" + 144 -> "sc-b1-10.scws-content.net" + else -> null + } + } +} + data class Moviedata( @JsonProperty("id") val id: Long, @JsonProperty("name") val name: String, @@ -55,9 +284,7 @@ data class Image( // @JsonProperty("proxy") val proxy: Proxy, // @JsonProperty("server") val server: Proxy ) - // Proxy is not used and crashes otherwise - //data class Proxy( // @JsonProperty("id") val id: Long, // @JsonProperty("type") val type: String, @@ -124,325 +351,3 @@ data class TrailerElement( @JsonProperty("proxy_default_id") val proxyDefaultID: Any? = null, @JsonProperty("scws_id") val scwsID: Any? = null ) - - -class StreamingcommunityProvider: MainAPI() { - override var lang = "it" - override var mainUrl = "https://streamingcommunity.online" - override var name = "StreamingCommunity" - override val hasMainPage = true - override val hasChromecastSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - - private fun translatenumber(num: Int): Int? { - return when (num) { - 67 -> 1 - 71 -> 2 - 72 -> 3 - 73 -> 4 - 74 -> 5 - 75 -> 6 - 76 -> 7 - 77 -> 8 - 78 -> 9 - 79 -> 10 - 133 -> 11 - else -> null - } - } - - private fun translateip(num: Int): String? { - return when (num) { - 16 -> "sc-b1-01.scws-content.net" - 17 -> "sc-b1-02.scws-content.net" - 18 -> "sc-b1-03.scws-content.net" - 85 -> "sc-b1-04.scws-content.net" - 95 -> "sc-b1-05.scws-content.net" - 117 -> "sc-b1-06.scws-content.net" - 141 -> "sc-b1-07.scws-content.net" - 142 -> "sc-b1-08.scws-content.net" - 143 -> "sc-b1-09.scws-content.net" - 144 -> "sc-b1-10.scws-content.net" - else -> null - } - } - -// companion object { -// val posterMap = hashMapOf() -// } - - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val items = ArrayList() - val document = app.get(mainUrl).document - document.select("slider-title").subList(0, 3).map { it -> - if (it.attr("slider-name") != "In arrivo") { - val films = it.attr("titles-json") - val lista = mutableListOf() - val videoData = parseJson>(films) - - videoData.subList(0, 12).apmap { searchr -> - val id = searchr.id - val name = searchr.slug - val img = searchr.images[0].url - val number = translatenumber(searchr.images[0].serverID.toInt()) - val ip = translateip(searchr.images[0].proxyID.toInt()) - val posterurl = "https://$ip/images/$number/$img" - val videourl = "$mainUrl/titles/$id-$name" - //posterMap[videourl] = posterurl - val data = app.post("$mainUrl/api/titles/preview/$id", referer = mainUrl).text - val datajs = parseJson(data) - val type: TvType = if (datajs.type == "movie") { - TvType.Movie - } else { - TvType.TvSeries - } - - lista.add( - MovieSearchResponse( - datajs.name, - videourl, - this.name, - type, - posterurl, - datajs.releaseDate.substringBefore("-").filter { it.isDigit() } - .toIntOrNull(), - null, - ) - ) - } - items.add(HomePageList(it.attr("slider-name"), lista)) - } - } - if (items.size <= 0) throw ErrorLoadingException() - return HomePageResponse(items) - } - - override suspend fun search(query: String): List { - val queryformatted = query.replace(" ", "%20") - val url = "$mainUrl/search?q=$queryformatted" - val document = app.get(url).document - - val films = - document.selectFirst("the-search-page")!!.attr("records-json").replace(""", """"""") - - val searchresults = parseJson>(films) - return searchresults.apmap { result -> - val id = result.id - val name = result.slug - val img = result.images[0].url - val number = translatenumber(result.images[0].serverID.toInt()) - val ip = translateip(result.images[0].proxyID.toInt()) - val data = app.post("$mainUrl/api/titles/preview/$id", referer = mainUrl).text - val datajs = parseJson(data) - val posterurl = "https://$ip/images/$number/$img" - val videourl = "$mainUrl/titles/$id-$name" - //posterMap[videourl] = posterurl - if (datajs.type == "movie") { - val type = TvType.Movie - MovieSearchResponse( - datajs.name, - videourl, - this.name, - type, - posterurl, - datajs.releaseDate.substringBefore("-").filter { it.isDigit() }.toIntOrNull(), - null, - ) - } else { - val type = TvType.TvSeries - TvSeriesSearchResponse( - datajs.name, - videourl, - this.name, - type, - posterurl, - datajs.releaseDate.substringBefore("-").filter { it.isDigit() }.toIntOrNull(), - null, - ) - } - - } - - } - - override suspend fun load(url: String): LoadResponse { - - val document = app.get(url).document - val poster = Regex("url\\('(.*)'").find(document.selectFirst("div.title-wrap")?.attributes() - ?.get("style") ?: "")?.groupValues?.last() //posterMap[url] - val id = url.substringBefore("-").filter { it.isDigit() } - val data = app.post("$mainUrl/api/titles/preview/$id", referer = mainUrl).text - - val datajs = parseJson(data) - val type: TvType = if (datajs.type == "movie") { - TvType.Movie - } else { - TvType.TvSeries - } - val trailerinfojs = document.select("slider-trailer").attr("videos") - val trailerinfo = parseJson>(trailerinfojs) - val trailerurl: String? = if (trailerinfo.isNotEmpty()) { - "https://www.youtube.com/watch?v=${trailerinfo[0].url}" - } else { - null - } - - val year = datajs.releaseDate.substringBefore("-") - - val correlatijs = document.selectFirst("slider-title")!!.attr("titles-json") - val listacorr = mutableListOf() - val correlatidata = parseJson>(correlatijs) - val number : Int = if (correlatidata.size<=15) {correlatidata.size} else correlatidata.size-15 - - correlatidata.take(number).apmap { searchr -> - val idcorr = searchr.id - val name = searchr.slug - val img = searchr.images[0].url - val number = translatenumber(searchr.images[0].serverID.toInt()) - val ip = translateip(searchr.images[0].proxyID.toInt()) - val datacorrel = app.post("$mainUrl/api/titles/preview/$idcorr", referer = mainUrl).text - val datajscorrel = parseJson(datacorrel) - val videourl = "$mainUrl/titles/$idcorr-$name" - val posterurl = "https://$ip/images/$number/$img" - - //posterMap[videourl] = posterurl - val typecorr: TvType = if (datajscorrel.type == "movie") { - TvType.Movie - } else { - TvType.TvSeries - } - - listacorr.add( - MovieSearchResponse( - datajscorrel.name, - videourl, - this.name, - typecorr, - posterurl, - datajscorrel.releaseDate.substringBefore("-").filter { it.isDigit() } - .toIntOrNull(), - null, - ) - ) - } - - if (type == TvType.TvSeries) { - - val name = datajs.name - val episodeList = arrayListOf() - - val episodes = - Html.fromHtml(document.selectFirst("season-select")!!.attr("seasons")).toString() - val jsonEpisodes = parseJson>(episodes) - - jsonEpisodes.map { seasons -> - val stagione = seasons.number.toInt() - val sid = seasons.title_id - val episodio = seasons.episodes - episodio.map { ep -> - val href = "$mainUrl/watch/$sid?e=${ep.id}" - val postimage = if (ep.images.isNotEmpty()) { - ep.images.first().originalURL - } else { - "" - } - episodeList.add( - - newEpisode(href) { - this.name = ep.name - this.season = stagione - this.episode = ep.number.toInt() - this.description = ep.plot - this.posterUrl = postimage - } - ) - } - } - - - if (episodeList.isEmpty()) throw ErrorLoadingException("No Seasons Found") - - return newTvSeriesLoadResponse(name, url, type, episodeList) { - this.posterUrl = poster - this.year = year.filter { it.isDigit() }.toInt() - this.plot = document.selectFirst("div.plot-wrap > p")!!.text() - this.duration = datajs.runtime?.toInt() - this.rating = (datajs.votes[0].average.toFloatOrNull()?.times(1000))?.toInt() - this.tags = datajs.genres.map { it.name } - addTrailer(trailerurl) - this.recommendations = listacorr - } - - - } else { - - return newMovieLoadResponse( - document.selectFirst("div > div > h1")!!.text(), - document.select("a.play-hitzone").attr("href"), - type, - document.select("a.play-hitzone").attr("href") - ) { - posterUrl = fixUrlNull(poster) - this.year = year.filter { it.isDigit() }.toInt() - this.plot = document.selectFirst("p.plot")!!.text() - this.rating = datajs.votes[0].average.toFloatOrNull()?.times(1000)?.toInt() - this.tags = datajs.genres.map { it.name } - this.duration = datajs.runtime?.toInt() - addTrailer(trailerurl) - this.recommendations = listacorr - } - - } - } - - - private suspend fun getM3u8Qualities( - m3u8Link: String, - referer: String, - qualityName: String, - ): List { - return M3u8Helper.generateM3u8( - this.name, - m3u8Link, - referer, - name = "${this.name} - $qualityName" - ) - } - - - override suspend fun loadLinks( - data: String, - isCasting: Boolean, - subtitleCallback: (SubtitleFile) -> Unit, - callback: (ExtractorLink) -> Unit - ): Boolean { - val ip = app.get("https://api.ipify.org/").text - val videors = app.get(data).document - val scwsidjs = videors.select("video-player").attr("response").replace(""", """"""") - val jsn = JSONObject(scwsidjs) - val scwsid = jsn.getString("scws_id") - val expire = (System.currentTimeMillis() / 1000 + 172800).toString() - - val token0 = "$expire$ip Yc8U6r8KjAKAepEA".toByteArray() - val token1 = MessageDigest.getInstance("MD5").digest(token0) - val token2 = base64Encode(token1) - val token = token2.replace("=", "").replace("+", "-").replace("/", "_") - - val link = "https://scws.work/master/$scwsid?token=$token&expires=$expire&n=1" - - callback.invoke( - ExtractorLink( - name, - name, - link, - isM3u8 = true, - referer = mainUrl, - quality = Qualities.Unknown.value - ) - ) - return true - } -}