diff --git a/AltadefinizioneProvider/build.gradle.kts b/AltadefinizioneProvider/build.gradle.kts index 2d177e7..ed63616 100644 --- a/AltadefinizioneProvider/build.gradle.kts +++ b/AltadefinizioneProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 3 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: diff --git a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt index 6c7a92e..252911d 100644 --- a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt +++ b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt @@ -1,15 +1,18 @@ package com.lagradost import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addActors +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor -import com.lagradost.cloudstream3.utils.AppUtils.html +import okhttp3.FormBody +import org.jsoup.nodes.Element class AltadefinizioneProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://altadefinizione.tienda" + override var mainUrl = "https://altadefinizione.navy" override var name = "Altadefinizione" override val hasMainPage = true override val hasChromecastSupport = true @@ -25,109 +28,76 @@ class AltadefinizioneProvider : MainAPI() { override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val url = request.data + page - val soup = app.get(url).document - val home = soup.select("div.box").map { - val title = it.selectFirst("img")!!.attr("alt") - val link = it.selectFirst("a")!!.attr("href") - val image = mainUrl + it.selectFirst("img")!!.attr("src") - val quality = getQualityFromString(it.selectFirst("span")!!.text()) - - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - image, - null, - null, - quality, - ) + val home = soup.select("div.box").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = true) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst("img")?.attr("alt") ?: return null + val link = this.selectFirst("a")?.attr("href") ?: return null + val image = mainUrl + this.selectFirst("img")?.attr("src") + val quality = getQualityFromString(this.selectFirst("span")?.text()) + return newMovieSearchResponse(title, link, TvType.Movie) { + this.posterUrl = image + this.quality = quality } - return newHomePageResponse(request.name, home) } override suspend fun search(query: String): List { + val body = FormBody.Builder() + .addEncoded("do", "search") + .addEncoded("subaction", "search") + .addEncoded("story", query) + .addEncoded("sortby", "news_read") + .build() + val doc = app.post( - "$mainUrl/index.php", data = mapOf( - "do" to "search", - "subaction" to "search", - "story" to query, - "sortby" to "news_read" - ) + "$mainUrl/index.php", + requestBody = body ).document - return doc.select("div.box").map { - val title = it.selectFirst("img")!!.attr("alt") - val link = it.selectFirst("a")!!.attr("href") - val image = mainUrl + it.selectFirst("img")!!.attr("src") - val quality = getQualityFromString(it.selectFirst("span")!!.text()) - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - image, - null, - null, - quality, - ) + return doc.select("div.box").mapNotNull { + it.toSearchResult() } } override suspend fun load(url: String): LoadResponse { - val page = app.get(url) - val document = page.document - val title = document.selectFirst(" h1 > a")!!.text().replace("streaming", "") - val description = document.select("#sfull").toString().substringAfter("altadefinizione") - .substringBeforeLast("fonte trama").html().toString() - val rating = null - - val year = document.selectFirst("#details > li:nth-child(2)")!!.childNode(2).toString() - .filter { it.isDigit() }.toInt() - - val poster = fixUrl(document.selectFirst("div.thumbphoto > img")!!.attr("src")) - - val recomm = document.select("ul.related-list > li").map { - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = mainUrl + it.selectFirst("img")!!.attr("src") - val name = it.selectFirst("img")!!.attr("alt") - MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - posterUrl, - null - ) + val document = app.get(url).document + val title = document.selectFirst(" h1 > a")?.text()?.replace("streaming", "") + ?: throw ErrorLoadingException("No Title found") + val description = document.select("#sfull").textNodes().first { it.text().trim().isNotEmpty() }.text().trim() + val rating = document.select("span.rateIMDB").text().substringAfter(" ") + val year = document.selectFirst("#details")?.select("li") + ?.firstOrNull { it.select("label").text().contains("Anno") } + ?.text()?.substringAfter(" ")?.toIntOrNull() + val poster = fixUrl(document.selectFirst("div.thumbphoto > img")?.attr("src")?: throw ErrorLoadingException("No Poster found") ) + val recomm = document.select("ul.related-list > li").mapNotNull { + it.toSearchResult() } - - - val actors: List = + val actors: List = document.select("#staring > a").map { - ActorData(actor = Actor(it.text())) + Actor(it.text()) } - val tags: List = document.select("#details > li:nth-child(1) > a").map { it.text() } - - val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")?.attr("src") - + val trailerUrl = document.selectFirst("#showtrailer > div > div > iframe")?.attr("src") return newMovieLoadResponse( title, url, TvType.Movie, url ) { - posterUrl = fixUrlNull(poster) this.year = year this.plot = description - this.rating = rating this.recommendations = recomm - this.duration = null - this.actors = actors this.tags = tags - addTrailer(trailerurl) + addActors(actors) + addPoster(poster) + addRating(rating) + addTrailer(trailerUrl) } } @@ -153,7 +123,6 @@ class AltadefinizioneProvider : MainAPI() { loadExtractor(fixUrl(it.attr("data-link")), data, subtitleCallback, callback) } } - return true } } \ No newline at end of file diff --git a/AniPlayProvider/build.gradle.kts b/AniPlayProvider/build.gradle.kts index 8929f13..2247b83 100644 --- a/AniPlayProvider/build.gradle.kts +++ b/AniPlayProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 4 cloudstream { diff --git a/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt index 9c5b68b..1d07a4d 100644 --- a/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt +++ b/AniPlayProvider/src/main/kotlin/com/lagradost/AniPlayProvider.kt @@ -15,6 +15,7 @@ class AniPlayProvider : MainAPI() { override var name = "AniPlay" override var lang = "it" override val hasMainPage = true + override val hasQuickSearch = true private val dubIdentifier = " (ITA)" override val supportedTypes = setOf( @@ -50,11 +51,15 @@ class AniPlayProvider : MainAPI() { data class ApiMainPageAnime( @JsonProperty("animeId") val id: Int, + @JsonProperty("id") val id2: Int, @JsonProperty("episodeNumber") val episode: String?, - @JsonProperty("animeTitle") val title: String, - @JsonProperty("animeType") val type: String, + @JsonProperty("animeTitle") val title: String?, + @JsonProperty("title") val title2: String?, + @JsonProperty("animeType") val type: String?, + @JsonProperty("type") val type2: String?, @JsonProperty("fullHd") val fullHD: Boolean, - @JsonProperty("animeVerticalImages") val posters: List + @JsonProperty("animeVerticalImages") val posters: List?, + @JsonProperty("verticalImages") val posters2: List? ) data class ApiSearchResult( @@ -106,6 +111,7 @@ class AniPlayProvider : MainAPI() { @JsonProperty("status") val status: String, @JsonProperty("genres") val genres: List, @JsonProperty("verticalImages") val posters: List, + @JsonProperty("horizontalImages") val horizontalPosters: List, @JsonProperty("listWebsites") val websites: List, @JsonProperty("episodes") val episodes: List, @JsonProperty("seasons") val seasons: List? @@ -114,25 +120,47 @@ class AniPlayProvider : MainAPI() { data class ApiEpisodeUrl( @JsonProperty("videoUrl") val url: String ) + override val mainPage = mainPageOf( + Pair("$mainUrl/api/home/latest-episodes?page=", "Ultime uscite"), + Pair("$mainUrl/api/anime/advanced-search?size=36&sort=views,desc&sort=id&page=", "I più popolari"), + ) + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { - override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val response = parseJson>(app.get("$mainUrl/api/home/latest-episodes?page=0").text) + val response = parseJson>(app.get(request.data + page).text) - val results = response.map{ + val results = response.mapNotNull{ + val title = it.title?:it.title2?: return@mapNotNull null + val isDub = isDub(title) + val id = if (it.id == 0) it.id2 else it.id + newAnimeSearchResponse( + name = if (isDub) title.replace(dubIdentifier, "") else title, + url = "$mainUrl/api/anime/$id", + type = getType(it.type?:it.type2), + ){ + addDubStatus(isDub, it.episode?.toIntOrNull()) + this.posterUrl = (it.posters?:it.posters2!!).first().posterUrl + this.quality = if (it.fullHD) SearchQuality.HD else null + } + } + return newHomePageResponse(request.name, results) + } + + override suspend fun quickSearch(query: String): List? { + val response = parseJson>(app.get("$mainUrl/api/anime/search?query=$query").text) + + return response.map { val isDub = isDub(it.title) + newAnimeSearchResponse( name = if (isDub) it.title.replace(dubIdentifier, "") else it.title, url = "$mainUrl/api/anime/${it.id}", type = getType(it.type), ){ - addDubStatus(isDub, it.episode?.toIntOrNull()) + addDubStatus(isDub) this.posterUrl = it.posters.first().posterUrl - this.quality = if (it.fullHD) SearchQuality.HD else null } } - return HomePageResponse(listOf(HomePageList("Ultime uscite",results))) } - override suspend fun search(query: String): List { val response = parseJson>(app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").text) @@ -168,7 +196,7 @@ class AniPlayProvider : MainAPI() { this.plot = response.plot this.tags = tags this.showStatus = getStatus(response.status) - addPoster(response.posters.first().posterUrl) + addPoster(response.horizontalPosters.firstOrNull()?.posterUrl) addEpisodes(if (isDub) DubStatus.Dubbed else DubStatus.Subbed, episodes) addMalId(malId) addAniListId(aniListId) @@ -185,22 +213,6 @@ class AniPlayProvider : MainAPI() { val episode = parseJson(app.get(data).text) - if(episode.url.contains(".m3u8")){ - val m3u8Helper = M3u8Helper() - val streams = m3u8Helper.m3u8Generation(M3u8Helper.M3u8Stream(episode.url,Qualities.Unknown.value), false) - - streams.forEach { - callback.invoke( - ExtractorLink( - name, - name, - it.streamUrl, - referer = mainUrl, - quality = it.quality ?: Qualities.Unknown.value, - isM3u8 = it.streamUrl.contains(".m3u8"))) } - return true - } - callback.invoke( ExtractorLink( name, @@ -208,7 +220,7 @@ class AniPlayProvider : MainAPI() { episode.url, referer = mainUrl, quality = Qualities.Unknown.value, - isM3u8 = false, + isM3u8 = episode.url.contains(".m3u8"), ) ) return true diff --git a/AnimeSaturnProvider/build.gradle.kts b/AnimeSaturnProvider/build.gradle.kts index 09242e2..d27f120 100644 --- a/AnimeSaturnProvider/build.gradle.kts +++ b/AnimeSaturnProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 3 cloudstream { diff --git a/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt index dadb2b5..f5647af 100644 --- a/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt +++ b/AnimeSaturnProvider/src/main/kotlin/com/lagradost/AnimeSaturnProvider.kt @@ -1,19 +1,23 @@ package com.lagradost +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities import org.jsoup.nodes.Element class AnimeSaturnProvider : MainAPI() { - override var mainUrl = "https://www.animesaturn.cc" + override var mainUrl = "https://www.animesaturn.in" override var name = "AnimeSaturn" override var lang = "it" override val hasMainPage = true + override val hasQuickSearch = true override val supportedTypes = setOf( TvType.Anime, @@ -21,6 +25,12 @@ class AnimeSaturnProvider : MainAPI() { TvType.OVA ) + private data class QuickSearchParse( + @JsonProperty("link") val link: String, + @JsonProperty("image") val image: String, + @JsonProperty("name") val name: String + ) + companion object { fun getStatus(t: String?): ShowStatus? { return when (t?.lowercase()) { @@ -31,8 +41,22 @@ class AnimeSaturnProvider : MainAPI() { } } - private fun Element.toSearchResult(): AnimeSearchResponse { + private fun Element.toSearchResponse(): AnimeSearchResponse? { + val url = this.select("a").first()?.attr("href") + ?: return null + val title = this.select("a[title]").first()?.attr("title")?.removeSuffix("(ITA)") + ?: return null + val posterUrl = this.select("img.new-anime").first()!!.attr("src") + val isDubbed = this.select("a[title]").first()?.attr("title")?.contains("(ITA)") + ?: false + return newAnimeSearchResponse(title, url, TvType.Anime){ + addDubStatus(isDubbed) + addPoster(posterUrl) + } + } + + private fun Element.toSearchResult(): AnimeSearchResponse { var title = this.select("a.badge-archivio").first()!!.text() var isDubbed = false @@ -42,19 +66,18 @@ class AnimeSaturnProvider : MainAPI() { } val url = this.select("a.badge-archivio").first()!!.attr("href") - val posterUrl = this.select("img.locandina-archivio[src]").first()!!.attr("src") return newAnimeSearchResponse(title, url, TvType.Anime) { addDubStatus(isDubbed) - this.posterUrl = posterUrl + addPoster(posterUrl) } } private fun Element.toEpisode(): Episode? { var episode = this.text().split(" ")[1] - if(episode.contains(".")) return null - if(episode.contains("-")) + if (episode.contains(".")) return null + if (episode.contains("-")) episode = episode.split("-")[0] return Episode( @@ -65,32 +88,53 @@ class AnimeSaturnProvider : MainAPI() { } override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val list = mutableListOf() + + val documentLastEpisode = app.get("$mainUrl/fetch_pages.php?request=episodes", + headers = mapOf("x-requested-with" to "XMLHttpRequest") + ).document + val lastedEpisode = documentLastEpisode.select(".anime-card").mapNotNull { + val url = it.select("a").first()?.attr("href")?.let { href -> + href.split("-ep-")[0].replace("/ep/", "/anime/") + } ?: return@mapNotNull null + val title = it.select("a").first()?.attr("title")?.removeSuffix(" (ITA)") + ?: return@mapNotNull null + val posterUrl = it.select("img").first()?.attr("src") + + val dub = it.select("a").first()?.attr("title")?.contains("(ITA)") ?: false + val episode = it.select(".anime-episode").text().split(" ").last().toIntOrNull() + + newAnimeSearchResponse(title, url, TvType.Anime) { + addPoster(posterUrl) + addDubStatus(dub, episode) + } + } + list.add(HomePageList("Ultimi episodi", lastedEpisode, isHorizontalImages = true)) + val document = app.get(mainUrl).document - val list = ArrayList() document.select("div.container:has(span.badge-saturn)").forEach { val tabName = it.select("span.badge-saturn").first()!!.text() if (tabName.equals("Ultimi episodi")) return@forEach - val results = ArrayList() - it.select(".main-anime-card").forEach { card -> - var title = card.select("a[title]").first()!!.attr("title") - var isDubbed = false - if(title.contains(" (ITA)")){ - title = title.replace(" (ITA)", "") - isDubbed = true - } - val posterUrl = card.select("img.new-anime").first()!!.attr("src") - val url = card.select("a").first()!!.attr("href") - results.add(newAnimeSearchResponse(title, url, TvType.Anime){ - addDubStatus(isDubbed) - this.posterUrl = posterUrl - }) + val results = it.select(".main-anime-card").mapNotNull { card -> + card.toSearchResponse() } list.add(HomePageList(tabName, results)) } return HomePageResponse(list) } + override suspend fun quickSearch(query: String): List? { + val quickSearchJ = app.get("$mainUrl/index.php?search=1&key=$query").text + return tryParseJson>(quickSearchJ)?.map { + newAnimeSearchResponse(it.name.removeSuffix("(ITA)"), it.link, TvType.Anime) { + addDubStatus(it.name.contains(" (ITA)")) + addPoster(it.image) + } + } + + } + override suspend fun search(query: String): List { val document = app.get("$mainUrl/animelist?search=$query").document return document.select("div.item-archivio").map { @@ -99,11 +143,10 @@ class AnimeSaturnProvider : MainAPI() { } override suspend fun load(url: String): LoadResponse { - val document = app.get(url).document - val title = document.select("img.cover-anime").first()!!.attr("alt") - val japTitle = document.select("div.box-trasparente-alternativo").first()!!.text() + val title = document.select("img.cover-anime").first()!!.attr("alt").removeSuffix("(ITA)") + val japTitle = document.select("div.box-trasparente-alternativo").first()!!.text().removeSuffix("(ITA)") val posterUrl = document.select("img.cover-anime[src]").first()!!.attr("src") var malId : Int? = null var aniListId : Int? = null @@ -116,8 +159,9 @@ class AnimeSaturnProvider : MainAPI() { } val plot = document.select("div#shown-trama").first()?.text() - val tags = document.select("a.generi-as").map { it.text() } + val isDubbed = document.select("div.anime-title-as").first()!!.text().contains("(ITA)") + val trailerUrl = document.select("#trailer-iframe").first()?.attr("src") val details : List? = document.select("div.container:contains(Stato: )").first()?.text()?.split(" ") var status : String? = null @@ -125,8 +169,6 @@ class AnimeSaturnProvider : MainAPI() { var year : String? = null var score : String? = null - val isDubbed = document.select("div.anime-title-as").first()!!.text().contains("(ITA)") - if (!details.isNullOrEmpty()) { details.forEach { val index = details.indexOf(it) +1 @@ -142,6 +184,10 @@ class AnimeSaturnProvider : MainAPI() { val episodes = document.select("a.bottone-ep").mapNotNull{ it.toEpisode() } + val recommendations = document.select("#carousel > .main-anime-card").mapNotNull { + it.toSearchResponse() + } + return newAnimeLoadResponse(title, url, TvType.Anime) { this.engName = title this.japName = japTitle @@ -155,6 +201,8 @@ class AnimeSaturnProvider : MainAPI() { addMalId(malId) addAniListId(aniListId) addDuration(duration) + addTrailer(trailerUrl) + this.recommendations = recommendations } } @@ -164,20 +212,18 @@ class AnimeSaturnProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - val page = app.get(data).document val episodeLink = page.select("div.card-body > a[href]").find {it1 -> it1.attr("href").contains("watch?") - }?.attr("href") + }?.attr("href") ?: return false - val episodePage = app.get(episodeLink!!).document + val episodePage = app.get(episodeLink).document val episodeUrl: String? var isM3U8 = false - if(episodePage.select("video.afterglow > source").isNotEmpty()) //Old player + if (episodePage.select("video.afterglow > source").isNotEmpty()) // Old player episodeUrl = episodePage.select("video.afterglow > source").first()!!.attr("src") - - else{ //New player + else { // New player val script = episodePage.select("script").find { it.toString().contains("jwplayer('player_hls').setup({") }!!.toString() @@ -185,7 +231,6 @@ class AnimeSaturnProvider : MainAPI() { isM3U8 = true } - callback.invoke( ExtractorLink( name, diff --git a/AnimeWorldProvider/build.gradle.kts b/AnimeWorldProvider/build.gradle.kts index 35d5f26..58eac0a 100644 --- a/AnimeWorldProvider/build.gradle.kts +++ b/AnimeWorldProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 3 cloudstream { diff --git a/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt index 3d454cb..2e1f040 100644 --- a/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt +++ b/AnimeWorldProvider/src/main/kotlin/com/lagradost/AnimeWorldProvider.kt @@ -21,6 +21,7 @@ class AnimeWorldProvider : MainAPI() { override var name = "AnimeWorld" override var lang = "it" override val hasMainPage = true + override val hasQuickSearch = true override val supportedTypes = setOf( TvType.Anime, @@ -30,7 +31,7 @@ class AnimeWorldProvider : MainAPI() { companion object { private var cookies = emptyMap() - + private lateinit var token : String // Disabled authentication as site did private suspend fun request(url: String): NiceResponse { // if (cookies.isEmpty()) { @@ -131,9 +132,12 @@ class AnimeWorldProvider : MainAPI() { } override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { - val document = request(mainUrl).document + val pagedata = request(mainUrl) + val document = pagedata.document val list = ArrayList() - + token = document.getElementById("csrf-token")?.attr("content")?:"" + cookies = pagedata.cookies + val widget = document.select(".widget.hotnew") widget.select(".tabs [data-name=\"sub\"], .tabs [data-name=\"dub\"]").forEach { tab -> val tabId = tab.attr("data-name") @@ -154,6 +158,39 @@ class AnimeWorldProvider : MainAPI() { return HomePageResponse(list) } + data class searchJson( + @JsonProperty("animes") val animes: List + ) + data class animejson( + @JsonProperty("name") val name: String, + @JsonProperty("image") val image: String, + @JsonProperty("link") val link: String, + @JsonProperty("animeTypeName") val type: String, + @JsonProperty("language") val language: String, + @JsonProperty("jtitle") val otherTitle: String, + @JsonProperty("identifier") val id: String + ) + + override suspend fun quickSearch(query: String): List? { + val document = app.post("https://www.animeworld.tv/api/search/v2?keyword=${query}", referer = mainUrl, cookies = cookies, headers = mapOf("csrf-token" to token)).text + + return tryParseJson(document)?.animes?.map { anime-> + val type = when (anime.type) { + "Movie" -> TvType.AnimeMovie + "OVA" -> TvType.OVA + else -> TvType.Anime + } + val dub = when (anime.language) { + "it" -> true + else -> false + } + newAnimeSearchResponse(anime.name, "$mainUrl/play/${anime.link}.${anime.id}", type) { + addDubStatus(dub) + this.otherName = anime.otherTitle + this.posterUrl = anime.image + } + } + } override suspend fun search(query: String): List { val document = request("$mainUrl/search?keyword=$query").document return document.select(".film-list > .item").map { diff --git a/CalcioStreamingProvider/build.gradle.kts b/CalcioStreamingProvider/build.gradle.kts index 7bf0bc2..a729e56 100644 --- a/CalcioStreamingProvider/build.gradle.kts +++ b/CalcioStreamingProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: diff --git a/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt index 857d054..e52a29b 100644 --- a/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt +++ b/CalcioStreamingProvider/src/main/kotlin/com/lagradost/CalcioStreamingProvider.kt @@ -2,7 +2,7 @@ package com.lagradost import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.* - +import org.jsoup.nodes.Document class CalcioStreamingProvider : MainAPI() { override var lang = "it" @@ -58,12 +58,21 @@ class CalcioStreamingProvider : MainAPI() { poster, plot = Matchstart ) - - } + private fun matchFound(document: Document) : Boolean { + return Regex(""""((.|\n)*?).";""").containsMatchIn( + getAndUnpack( + document.toString() + )) + } - + private fun getUrl(document: Document):String{ + return Regex(""""((.|\n)*?).";""").find( + getAndUnpack( + document.toString() + ))!!.value.replace("""src="""", "").replace(""""""", "").replace(";", "") + } private suspend fun extractVideoLinks( url: String, @@ -71,25 +80,28 @@ class CalcioStreamingProvider : MainAPI() { ) { val document = app.get(url).document document.select("button.btn").forEach { button -> - val link1 = button.attr("data-link") - val doc2 = app.get(link1).document - val truelink = doc2.selectFirst("iframe")!!.attr("src") - val newpage = app.get(truelink, referer = link1).document - val streamurl = Regex(""""((.|\n)*?).";""").find( - getAndUnpack( - newpage.select("script")[6].childNode(0).toString() - ))!!.value.replace("""src="""", "").replace(""""""", "").replace(";", "") - - callback( - ExtractorLink( - this.name, - button.text(), - streamurl, - truelink, - quality = 0, - true - ) - ) + var link = button.attr("data-link") + var oldLink = link + var videoNotFound = true + while (videoNotFound) { + val doc = app.get(link).document + link = doc.selectFirst("iframe")?.attr("src") ?: break + val newpage = app.get(fixUrl(link), referer = oldLink).document + oldLink = link + if (newpage.select("script").size >= 6 && matchFound(newpage)){ + videoNotFound = false + callback( + ExtractorLink( + this.name, + button.text(), + getUrl(newpage), + fixUrl(link), + quality = 0, + true + ) + ) + } + } } } diff --git a/CasaCinemaProvider/build.gradle.kts b/CasaCinemaProvider/build.gradle.kts new file mode 100644 index 0000000..914b7eb --- /dev/null +++ b/CasaCinemaProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 3 + + +cloudstream { + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + language= "it" + authors = listOf("Forthe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=casacinema.lol&sz=%size%" +} diff --git a/CasaCinemaProvider/src/main/AndroidManifest.xml b/CasaCinemaProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CasaCinemaProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProvider.kt b/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProvider.kt new file mode 100644 index 0000000..e9533cd --- /dev/null +++ b/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProvider.kt @@ -0,0 +1,204 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.network.CloudflareKiller +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.ShortLink.unshorten +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.nodes.Element + +class CasaCinemaProvider : MainAPI() { // all providers must be an instance of MainAPI + override var mainUrl = "https://casacinema.lol/" + override var name = "CasaCinema" + override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries) + override val hasChromecastSupport = true + override var lang = "it" + override val hasMainPage = true + private val interceptor = CloudflareKiller() + + override val mainPage = + mainPageOf( + "$mainUrl/category/serie-tv/page/" to "Ultime Serie Tv", + "$mainUrl/category/film/page/" to "Ultimi Film", + ) + + private fun fixTitle(element: Element?): String { + return element?.text() + ?.trim() + ?.substringBefore("Streaming") + ?.replace("[HD]", "") + ?.replace("\\(\\d{4}\\)".toRegex(), "") + ?: "No Title found" + } + + private fun Element?.isMovie(): Boolean { + return (this + ?.text() ?: "") + .contains("\\(\\d{4}\\)".toRegex()) + } + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + + val url = request.data + page + + val soup = app.get(url, referer = mainUrl).document + val home = soup.select("ul.posts>li").mapNotNull { it.toSearchResult() } + val hasNext = soup.select("div.navigation>ul>li>a").last()?.text() == "Pagina successiva »" + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = hasNext) + } + + + override suspend fun search(query: String): List { + val queryFormatted = query.replace(" ", "+") + val url = "$mainUrl/?s=$queryFormatted" + val doc = app.get(url, referer = mainUrl, interceptor = interceptor).document + return doc.select("ul.posts>li").map { it.toSearchResult() } + } + + private fun Element.toSearchResult(): SearchResponse { + val title = fixTitle(this.selectFirst(".title")) + val isMovie = this.selectFirst(".title").isMovie() + val link = + this.selectFirst("a")?.attr("href") ?: throw ErrorLoadingException("No Link found") + + val quality = this.selectFirst("div.hd")?.text() + val posterUrl = this.selectFirst("a")?.attr("data-thumbnail") + + return if (isMovie) { + newMovieSearchResponse(title, link, TvType.Movie) { + addPoster(posterUrl) + quality?.let { addQuality(it) } + } + } else { + newTvSeriesSearchResponse(title, link, TvType.TvSeries) { + addPoster(posterUrl) + quality?.let { addQuality(it) } + } + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url, referer = mainUrl).document + val type = + if (document.select("div.seasons-wraper").isNotEmpty()) TvType.TvSeries + else TvType.Movie + val title = fixTitle(document.selectFirst("div.row > h1")) + val description = document.select("div.element").last()?.text() + val year = document.selectFirst("div.element>a.tag") + ?.text() + ?.substringBefore("-") + ?.substringAfter(",") + ?.filter { it.isDigit() } + val poster = document.selectFirst("img.thumbnail")?.attr("src") + val rating = document.selectFirst("div.rating>div.value")?.text()?.trim()?.toRatingInt() + val recomm = document.select("div.crp_related>ul>li").map { it.toRecommendResult() } + if (type == TvType.TvSeries) { + val episodeList = + document.select("div.accordion>div.accordion-item") + .map { element -> + val season = + element.selectFirst("li.s_title>span.season-title") + ?.text() + ?.toIntOrNull() + ?: 0 + element.select("div.episode-wrap").map { episode -> + episode.toEpisode(season) + } + } + .flatten() + + return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { + this.year = year?.toIntOrNull() + this.plot = description + this.recommendations = recomm + addPoster(poster) + addRating(rating) + } + } else { + val actors: List = + document.select("div.cast_wraper>ul>li").map { actordata -> + val actorName = actordata.selectFirst("strong")?.text() ?: "" + val actorImage: String = + actordata.selectFirst("figure>img")?.attr("src") ?: "" + ActorData(actor = Actor(actorName, image = actorImage)) + } + val data = document.select(".embed-player").map { it.attr("data-id") }.toJson() + return newMovieLoadResponse(title, data, TvType.Movie, data) { + this.year = year?.toIntOrNull() + this.plot = description + this.actors = actors + this.recommendations = recomm + addPoster(poster) + addRating(rating) + } + } + } + + private fun Element.toRecommendResult(): SearchResponse { + val title = + fixTitle(this.selectFirst("span.crp_title")) + val isMovie = this.selectFirst("span.crp_title").isMovie() + val link = + this.selectFirst("a")?.attr("href") ?: throw ErrorLoadingException("No Link found") + + val quality = + this.selectFirst("span.crp_title")?.text()?.substringAfter("[")?.substringBefore("]") + val posterUrl = this.selectFirst("img")?.attr("src") + + return if (isMovie) { + newMovieSearchResponse(title, link, TvType.Movie) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } else { + newTvSeriesSearchResponse(title, link, TvType.TvSeries) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } + } + + private fun Element.toEpisode(season: Int): Episode { + val data = + this.select("div.fix-table>table>tbody>tr>td>a[target=_blank]") + .map { it.attr("href") } + .toJson() // isecure.link + val epNum = + this.selectFirst("li.season-no") + ?.text() + ?.substringAfter("x") + ?.substringBefore(" ") + ?.filter { it.isDigit() } + .orEmpty().ifBlank { "0" } + + val epTitle = + this.selectFirst("li.other_link>a")?.text().orEmpty().ifBlank { + "Episodio $epNum" + } + val posterUrl = this.selectFirst("figure>img")?.attr("src") + return Episode(data, epTitle, season, epNum.toInt(), posterUrl = posterUrl) + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + parseJson>(data).map { videoUrl -> + loadExtractor(unshorten(videoUrl), data, subtitleCallback, callback) + } + return true + } +} diff --git a/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProviderPlugin.kt b/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProviderPlugin.kt new file mode 100644 index 0000000..98135e8 --- /dev/null +++ b/CasaCinemaProvider/src/main/kotlin/com/lagradost/CasaCinemaProviderPlugin.kt @@ -0,0 +1,14 @@ +package com.lagradost + +import android.content.Context +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin + +@CloudstreamPlugin +class CasaCinemaProviderPlugin : Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list + // directly. + registerMainAPI(CasaCinemaProvider()) + } +} diff --git a/CineBlog01Provider/build.gradle.kts b/CineBlog01Provider/build.gradle.kts new file mode 100644 index 0000000..d380ea3 --- /dev/null +++ b/CineBlog01Provider/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 3 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Adippe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + + iconUrl = "https://www.google.com/s2/favicons?domain=cineblog01.legal&sz=%size%" +} \ No newline at end of file diff --git a/CineBlog01Provider/src/main/AndroidManifest.xml b/CineBlog01Provider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/CineBlog01Provider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProvider.kt b/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProvider.kt new file mode 100644 index 0000000..cdd6ba4 --- /dev/null +++ b/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProvider.kt @@ -0,0 +1,128 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import okhttp3.FormBody +import org.jsoup.nodes.Element + +class CineBlog01Provider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://www.cineblog01.mom" + override var name = "CineBlog01" + override val hasMainPage = true + override val hasChromecastSupport = true + override var sequentialMainPage = true + override val supportedTypes = setOf( + TvType.Movie, + ) + override val mainPage = mainPageOf( + Pair("$mainUrl/page/", "Film Popolari"), + Pair("$mainUrl/film-sub-ita/page/", "Film Sub-ita") + ) + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val url = request.data + page + val soup = app.get(url).document + val home = soup.select("div.filmbox").mapNotNull { series -> + series.toSearchResult() + } + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = true) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = + this.selectFirst("img")?.attr("alt") ?: throw ErrorLoadingException("No Title found") + val link = + this.selectFirst("a")?.attr("href") ?: throw ErrorLoadingException("No Link found") + val posterUrl = fixUrl( + this.selectFirst("img")?.attr("src") ?: throw ErrorLoadingException("No Poster found") + ) + val quality = Regex("\\[([^\\]]*)]").find( + this.selectFirst("h1")?.text() ?: "" + )?.groupValues?.getOrNull(1) ?: "" + val year = Regex("\\(([^)]*)\\)").find( + this.selectFirst("h1")?.text() ?: "" + )?.groupValues?.getOrNull(1)?.toIntOrNull() + return newMovieSearchResponse( + title, + link, + TvType.TvSeries + ) { + this.year = year + addPoster(posterUrl) + addQuality(quality) + } + } + + override suspend fun search(query: String): List { + val body = FormBody.Builder() + .addEncoded("do", "search") + .addEncoded("subaction", "search") + .addEncoded("story", query) + .addEncoded("sortby", "news_read") + .build() + val doc = app.post( + "$mainUrl/index.php", + requestBody = body + ).document + + return doc.select("div.filmbox").mapNotNull { series -> + series.toSearchResult() + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + val title = document.selectFirst("div.imgrow > img")!!.attr("alt") + val description = document.selectFirst("div.fstory")?.text()?.removeSuffix(" +Info »") + ?.substringAfter("′ - ") + val year = document.selectFirst("div.filmboxfull") + ?.getElementsByAttributeValueContaining("href", "/anno/")?.text()?.toIntOrNull() + val poster = fixUrl(document.selectFirst("div.imgrow > img")!!.attr("src")) + val dataUrl = document.select("ul.mirrors-list__list > li").map { + it.select("a").attr("href") + }.drop(1).joinToString(",") + val trailerUrl = + document.select("iframe").firstOrNull { it.attr("src").contains("youtube") } + ?.attr("src") + ?.let { fixUrl(it) } + val tags = + document.selectFirst("#dle-content h4")?.text()?.substringBefore("- DURATA")?.trim() + ?.split(" / ") + val duration = Regex("DURATA (.*)′").find( + document.selectFirst("#dle-content h4")?.text() ?: "" + )?.groupValues?.last() + return newMovieLoadResponse( + title, + url, + TvType.Movie, + dataUrl = dataUrl + ) { + this.plot = description + this.year = year + this.posterUrl = poster + this.tags = tags + addTrailer(trailerUrl) + addDuration(duration) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val links = data.split(",") + links.map { url -> + loadExtractor(fixUrl(url), fixUrl(url), subtitleCallback, callback) + } + return true + } +} \ No newline at end of file diff --git a/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt b/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt new file mode 100644 index 0000000..be3aadd --- /dev/null +++ b/CineBlog01Provider/src/main/kotlin/com/lagradost/CineBlogProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class CineBlog01ProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(CineBlog01Provider()) + } +} \ No newline at end of file diff --git a/CineBlogProvider/build.gradle.kts b/CineBlogProvider/build.gradle.kts index 88ebf30..ed9e440 100644 --- a/CineBlogProvider/build.gradle.kts +++ b/CineBlogProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: diff --git a/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt index 7ab87cd..211ccdb 100644 --- a/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt +++ b/CineBlogProvider/src/main/kotlin/com/lagradost/CineBlogProvider.kt @@ -3,12 +3,14 @@ package com.lagradost import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor +import okhttp3.FormBody +import org.jsoup.nodes.Element class CineBlogProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://cb01.rip" - override var name = "CineBlog" + override var mainUrl = "https://cb01.li" + override var name = "CB01" override val hasMainPage = true override val hasChromecastSupport = true override val supportedTypes = setOf( @@ -29,116 +31,94 @@ class CineBlogProvider : MainAPI() { ): HomePageResponse { val url = request.data.replace("number", page.toString()) val soup = app.get(url, referer = url.substringBefore("page")).document - val home = soup.select("article.item").map { - val title = it.selectFirst("div.data > h3 > a")!!.text().substringBefore("(") - val link = it.selectFirst("div.poster > a")!!.attr("href") - val quality = getQualityFromString(it.selectFirst("span.quality")?.text()) - TvSeriesSearchResponse( - title, - link, - this.name, - TvType.Movie, - it.selectFirst("img")!!.attr("src"), - null, - null, - quality = quality - ) + val home = soup.select("article.item").mapNotNull { + it.toSearchResult() } - return newHomePageResponse(request.name, home) + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = true) + } + + private fun Element.toSearchResult(): SearchResponse{ + val title = this.selectFirst("div.data > h3 > a")?.text()?.substringBefore("(") ?: + this.selectFirst("a > img")?.attr("alt")?.substringBeforeLast("(") ?: + throw ErrorLoadingException("No Title found") + + val link = this.selectFirst("div.poster > a")?.attr("href") ?: + this.selectFirst("a")?.attr("href") ?: + throw ErrorLoadingException("No Link found") + + val quality = this.selectFirst("span.quality")?.text() + + val posterUrl = this.selectFirst("img")?.attr("src") ?: + this.selectFirst("a > img")?.attr("src") + + return newMovieSearchResponse(title, link, TvType.Movie){ + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } + + private fun Element.toEpisode(season: Int): Episode { + val href = this.selectFirst("div.episodiotitle > a")?.attr("href")?: throw ErrorLoadingException("No Link found") + val epNum = this.selectFirst("div.numerando")?.text()?.substringAfter("-")?.filter { it.isDigit() }?.toIntOrNull() + val epTitle = this.selectFirst("div.episodiotitle > a")?.text()?: throw ErrorLoadingException("No Title found") + val posterUrl = this.selectFirst("div.imagen > img")?.attr("src") + return Episode( + href, + epTitle, + season, + epNum, + posterUrl, + ) } override suspend fun search(query: String): List { - val queryformatted = query.replace(" ", "+") - val url = "$mainUrl?s=$queryformatted" + val queryFormatted = query.replace(" ", "+") + val url = "$mainUrl?s=$queryFormatted" val doc = app.get(url,referer= mainUrl ).document return doc.select("div.result-item").map { - val href = it.selectFirst("div.image > div > a")!!.attr("href") - val poster = it.selectFirst("div.image > div > a > img")!!.attr("src") - val name = it.selectFirst("div.details > div.title > a")!!.text().substringBefore("(") - MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - poster - ) - + it.toSearchResult() } } override suspend fun load(url: String): LoadResponse { - val page = app.get(url) - val document = page.document + val document = app.get(url).document val type = if (url.contains("film")) TvType.Movie else TvType.TvSeries - val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(") + val title = document.selectFirst("div.data > h1")?.text()?.substringBefore("(")?: throw ErrorLoadingException("No Title found") val description = document.select("#info > div.wp-content > p").html().toString() - val rating = null - var year = document.selectFirst(" div.data > div.extra > span.date")!!.text().substringAfter(",") - .filter { it.isDigit() } - if (year.length > 4) { - year = year.dropLast(4) + val year = document.selectFirst(" div.data > div.extra > span.date")?.text()?.substringAfter(",")?.filter { it.isDigit() }.let { it?.dropLast(4) } + val poster = + document.selectFirst("#dt_galery")?.selectFirst("a")?.attr("href")?.trim()?: + document.selectFirst("div.poster > img")?.attr("src") + val recommendations = document.select("#single_relacionados >article").map { + it.toSearchResult() } - val poster = document.selectFirst("div.poster > img")!!.attr("src") - - val recomm = document.select("#single_relacionados >article").map { - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = it.selectFirst("a > img")!!.attr("src") - val name = it.selectFirst("a > img")!!.attr("alt").substringBeforeLast("(") - MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - posterUrl - ) - - } - - if (type == TvType.TvSeries) { - - val episodeList = ArrayList() - document.select("#seasons > div").reversed().map { element -> - val season = element.selectFirst("div.se-q > span.se-t")!!.text().toInt() + val episodeList = document.select("#seasons > div").reversed().map { element -> + val season = element.selectFirst("div.se-q > span.se-t")?.text()?.toIntOrNull()?:throw ErrorLoadingException("No Season found") element.select("div.se-a > ul > li").filter { it -> it.text()!="There are still no episodes this season" }.map{ episode -> - val href = episode.selectFirst("div.episodiotitle > a")!!.attr("href") - val epNum =episode.selectFirst("div.numerando")!!.text().substringAfter("-").filter { it.isDigit() }.toIntOrNull() - val epTitle = episode.selectFirst("div.episodiotitle > a")!!.text() - val posterUrl = episode.selectFirst("div.imagen > img")!!.attr("src") - episodeList.add( - Episode( - href, - epTitle, - season, - epNum, - posterUrl, - ) - ) + episode.toEpisode(season) } - } - return TvSeriesLoadResponse( + }.flatten() + + return newTvSeriesLoadResponse( title, url, - this.name, type, - episodeList, - fixUrlNull(poster), - year.toIntOrNull(), - description, - null, - rating, - null, - null, - mutableListOf(), - recomm - ) + episodeList){ + this.recommendations = recommendations + this.year = year?.toIntOrNull() + this.plot = description + addPoster(poster) + } } else { val actors: List = - document.select("div.person").filter{ it -> it.selectFirst("div.img > a > img")?.attr("src")!!.contains("/no/cast.png").not()}.map { actordata -> - val actorName = actordata.selectFirst("div.data > div.name > a")!!.text() + document.select("div.person").filter{ it -> it.selectFirst("div.img > a > img")?.attr("src")?.contains("/no/cast.png")?.not()?:false}.map { actordata -> + val actorName = actordata.selectFirst("div.data > div.name > a")?.text()?:throw ErrorLoadingException("No Actor name found") val actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src") - val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text() + val roleActor = actordata.selectFirst("div.data > div.caracter")?.text() ActorData(actor = Actor(actorName, image = actorImage), roleString = roleActor ) } return newMovieLoadResponse( @@ -147,13 +127,11 @@ class CineBlogProvider : MainAPI() { type, url ) { - posterUrl = fixUrlNull(poster) - this.year = year.toIntOrNull() + this.recommendations = recommendations + this.year = year?.toIntOrNull() this.plot = description - this.rating = rating - this.recommendations = recomm - this.duration = null this.actors = actors + addPoster(poster) } } } @@ -167,11 +145,14 @@ class CineBlogProvider : MainAPI() { val doc = app.get(data).document val type = if( data.contains("film") ){"movie"} else {"tv"} val idpost=doc.select("#player-option-1").attr("data-post") - val test = app.post("$mainUrl/wp-admin/admin-ajax.php", headers = mapOf( + + val test = app.post("$mainUrl/wp-admin/admin-ajax.php", + headers = mapOf( "content-type" to "application/x-www-form-urlencoded; charset=UTF-8", "accept" to "*/*", "X-Requested-With" to "XMLHttpRequest", - ), data = mapOf( + ), + data = mapOf( "action" to "doo_player_ajax", "post" to idpost, "nume" to "1", @@ -179,9 +160,9 @@ class CineBlogProvider : MainAPI() { )) val url2= Regex("""src='((.|\\n)*?)'""").find(test.text)?.groups?.get(1)?.value.toString() - val trueUrl = app.get(url2, headers = mapOf("referer" to mainUrl)).url + val trueUrl = app.get(url2, headers = mapOf("Referer" to mainUrl)).url loadExtractor(trueUrl, data, subtitleCallback, callback) return true } -} \ No newline at end of file +} diff --git a/DokumentalneProvider/build.gradle.kts b/DokumentalneProvider/build.gradle.kts new file mode 100644 index 0000000..7248110 --- /dev/null +++ b/DokumentalneProvider/build.gradle.kts @@ -0,0 +1,24 @@ +// use an integer for version numbers +version = 1 + +cloudstream { + language = "pl" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Cloudburst") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 + tvTypes = listOf( + "Documentary" + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=dokumentalne.net&sz=%size%" +} diff --git a/DokumentalneProvider/src/main/AndroidManifest.xml b/DokumentalneProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/DokumentalneProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProvider.kt b/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProvider.kt new file mode 100644 index 0000000..1d41c85 --- /dev/null +++ b/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProvider.kt @@ -0,0 +1,99 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor +import org.jsoup.Jsoup +import org.jsoup.select.Elements +import android.util.Log + +open class DokumentalneProvider : MainAPI() { + override var mainUrl = "https://dokumentalne.net/" + override var name = "Dokumentalne.net" + override var lang = "pl" + override val hasMainPage = true + override val supportedTypes = setOf( + TvType.Documentary + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val document = app.get(mainUrl).document + val items = document.select(".body-content article.cactus-post-item").mapNotNull{ it -> + val a = it.selectFirst("h3 a") ?: return@mapNotNull null + val name = a.attr("title").trim() + val href = a.attr("href") + val img = it.selectFirst("img")?.attr("src") + newMovieSearchResponse( + name, + href, + TvType.Documentary + ) { + this.posterUrl = img + } + } + return HomePageResponse(listOf(HomePageList("Najnowsze", items, isHorizontalImages = true)), false) + } + + override suspend fun search(query: String): List { + val url = "$mainUrl/?s=$query" + val document = app.get(url).document + return document.select("article.cactus-post-item").mapNotNull{ it -> + val a = it.selectFirst("h3 a") ?: return@mapNotNull null + val name = a.attr("title").trim() + val href = a.attr("href") + val img = it.selectFirst("img")?.attr("src") + newMovieSearchResponse( + name, + href, + TvType.Documentary + ) { + this.posterUrl = img + } + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + + val embedUrl = document.selectFirst("iframe[allowfullscreen]")?.attr("src")?.let { it -> + return@let if (it.startsWith("//")) "https:$it" + else it + } + val title = document.select("h1.single-title").text().trim() + + val plot = document.select(".single-post-content p").text().trim() + + return newMovieLoadResponse(title, url, TvType.Documentary, embedUrl) { + this.plot = plot + this.recommendations = document.select(".post-list-in-single article.cactus-post-item").mapNotNull{ it -> + val a = it.selectFirst("h3 a") ?: return@mapNotNull null + val name = a.attr("title").trim() + val href = a.attr("href") + val img = it.selectFirst("img")?.attr("src") + newMovieSearchResponse( + name, + href, + TvType.Documentary + ) { + this.posterUrl = img + } + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + loadExtractor(data, subtitleCallback, callback) + return true + } +} + +data class LinkElement( + @JsonProperty("src") val src: String +) diff --git a/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProviderPlugin.kt b/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProviderPlugin.kt new file mode 100644 index 0000000..2218688 --- /dev/null +++ b/DokumentalneProvider/src/main/kotlin/com/lagradost/DokumentalneProviderPlugin.kt @@ -0,0 +1,13 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class DokumentalneProviderPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(DokumentalneProvider()) + } +} \ No newline at end of file diff --git a/EntrepeliculasyseriesProvider/src/main/kotlin/com/lagradost/EntrepeliculasyseriesProvider.kt b/EntrepeliculasyseriesProvider/src/main/kotlin/com/lagradost/EntrepeliculasyseriesProvider.kt index 7832cb9..ea7d259 100644 --- a/EntrepeliculasyseriesProvider/src/main/kotlin/com/lagradost/EntrepeliculasyseriesProvider.kt +++ b/EntrepeliculasyseriesProvider/src/main/kotlin/com/lagradost/EntrepeliculasyseriesProvider.kt @@ -5,7 +5,7 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor class EntrepeliculasyseriesProvider : MainAPI() { - override var mainUrl = "https://entrepeliculasyseries.nu" + override var mainUrl = "https://entrepeliculasyseries.nz" override var name = "EntrePeliculasySeries" override var lang = "es" override val hasMainPage = true diff --git a/EurostreamingProvider/build.gradle.kts b/EurostreamingProvider/build.gradle.kts index fa19a12..da7feb4 100644 --- a/EurostreamingProvider/build.gradle.kts +++ b/EurostreamingProvider/build.gradle.kts @@ -1,13 +1,12 @@ // use an integer for version numbers -version = 1 - +version = 3 cloudstream { language = "it" // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: @@ -23,4 +22,4 @@ cloudstream { iconUrl = "https://www.google.com/s2/favicons?domain=eurostreaming.social&sz=%size%" -} \ No newline at end of file +} diff --git a/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt b/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt index ec81f4e..e82bc73 100644 --- a/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt +++ b/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt @@ -5,70 +5,68 @@ import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.loadExtractor import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson - +import com.lagradost.cloudstream3.network.CloudflareKiller +import okhttp3.FormBody +import org.jsoup.nodes.Element class EurostreamingProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://eurostreaming.social" + override var mainUrl = "https://eurostreaming.expert" override var name = "Eurostreaming" override val hasMainPage = true override val hasChromecastSupport = true + private val interceptor = CloudflareKiller() override val supportedTypes = setOf( TvType.TvSeries ) override val mainPage = mainPageOf( - Pair("$mainUrl/serie-tv-archive/page/", "Ultime serie Tv"), - Pair("$mainUrl/animazione/page/", "Ultime serie Animazione"), - + "$mainUrl/serie-tv-archive/page/" to "Ultime serie Tv", + "$mainUrl/animazione/page/" to "Ultime serie Animazione", ) override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val url = request.data + page - val soup = app.get(url).document - val home = soup.select("div.post-thumb").map { - val title = it.selectFirst("img")!!.attr("alt") - val link = it.selectFirst("a")!!.attr("href") - val image = fixUrl(it.selectFirst("img")!!.attr("src")) - - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - image - ) + val soup = app.get(url, interceptor = interceptor).document + val home = soup.select("div.post-thumb").mapNotNull { + it.toSearchResult() + } + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = true) + } + + private fun Element.toSearchResult(): SearchResponse? { + val title = this.selectFirst("a")?.attr("title") ?: return null + val link = this.selectFirst("a")?.attr("href") ?: return null + val image = fixUrlNull(mainUrl + this.selectFirst("img")?.attr("src")?.trim()) + + return newTvSeriesSearchResponse(title, link, TvType.TvSeries){ + this.posterUrl = image + this.posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() } - return newHomePageResponse(request.name, home) } override suspend fun search(query: String): List { - val doc = app.post( - "$mainUrl/index.php", data = mapOf( - "do" to "search", - "subaction" to "search", - "story" to query, - "sortby" to "news_read" - ) - ).document - return doc.select("div.post-thumb").map { - val title = it.selectFirst("img")!!.attr("alt") - val link = it.selectFirst("a")!!.attr("href") - val image = mainUrl + it.selectFirst("img")!!.attr("src") + val body = FormBody.Builder() + .addEncoded("do", "search") + .addEncoded("subaction", "search") + .addEncoded("story", query) + .addEncoded("sortby", "news_read") + .build() - MovieSearchResponse( - title, - link, - this.name, - TvType.Movie, - image - ) + val doc = app.post( + "$mainUrl/index.php", + requestBody = body, + interceptor = interceptor + ).document + + return doc.select("div.post-thumb").mapNotNull { + it?.toSearchResult() } } override suspend fun load(url: String): LoadResponse { - val page = app.get(url) + val page = app.get(url, interceptor = interceptor) val document = page.document val title = document.selectFirst("h2")!!.text().replace("^([1-9+]]$","") val style = document.selectFirst("div.entry-cover")!!.attr("style") @@ -93,7 +91,8 @@ class EurostreamingProvider : MainAPI() { } } return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { - posterUrl = poster + this.posterUrl = poster + this.posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() } } diff --git a/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt b/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt index 4c60ace..180c752 100644 --- a/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt +++ b/FilmanProvider/src/main/kotlin/com/lagradost/FilmanProvider.kt @@ -52,7 +52,7 @@ class FilmanProvider : MainAPI() { } override suspend fun search(query: String): List { - val url = "$mainUrl/wyszukiwarka?phrase=$query" + val url = "$mainUrl/item?phrase=$query" val document = app.get(url).document val lists = document.select("#advanced-search > div") val movies = lists[1].select("#item-list > div:not(.clearfix)") diff --git a/FilmpertuttiProvider/build.gradle.kts b/FilmpertuttiProvider/build.gradle.kts index 0c1c8ba..ea939f5 100644 --- a/FilmpertuttiProvider/build.gradle.kts +++ b/FilmpertuttiProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 5 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: diff --git a/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt b/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt index 13a525c..6f4c71d 100644 --- a/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt +++ b/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt @@ -11,23 +11,25 @@ import com.lagradost.cloudstream3.utils.ShortLink import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element import com.lagradost.cloudstream3.utils.AppUtils.html +import com.lagradost.cloudstream3.network.CloudflareKiller class FilmpertuttiProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://filmpertutti.photo" - override var name = "Filmpertutti" + override var mainUrl = "https://filmpertutti.tips" + override var name = "FilmPerTutti" override val hasMainPage = true override val hasChromecastSupport = true override val supportedTypes = setOf( TvType.Movie, TvType.TvSeries ) - + override var sequentialMainPage = true + override var sequentialMainPageDelay: Long = 50 override val mainPage = mainPageOf( Pair("$mainUrl/category/film/page/", "Film Popolari"), Pair("$mainUrl/category/serie-tv/page/", "Serie Tv Popolari"), - Pair("$mainUrl/prime-visioni/", "Ultime uscite"), + Pair("$mainUrl/prime-visioni/", "Ultime uscite") ) override suspend fun getMainPage( @@ -35,7 +37,6 @@ class FilmpertuttiProvider : MainAPI() { request: MainPageRequest ): HomePageResponse { val url = request.data + page - val soup = app.get(url).document val home = soup.select("ul.posts > li").map { val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(") @@ -90,10 +91,8 @@ class FilmpertuttiProvider : MainAPI() { val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(") .substringBeforeLast("[") - val description = - document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString() - ?.html().toString() - + val descriptionindex = document.select("div.meta > div > div").indexOfFirst { it.getElementsContainingText("Trama").isNotEmpty() } + val description = document.select("div.meta > div > div")[descriptionindex +1].text() val rating = document.selectFirst("div.rating > div.value")?.text() @@ -105,8 +104,10 @@ class FilmpertuttiProvider : MainAPI() { ?.nextSibling() as Element?)?.text()?.substringAfterLast(" ") ?.filter { it.isDigit() }?.toIntOrNull() - - val poster = document.selectFirst("div.meta > div > img")?.attr("data-src") + val horizontalPosterData = document.selectFirst("body > main")?.attr("style")?:"" + val poster = + Regex("url\\('(.*)'").find(horizontalPosterData)?.groups?.lastOrNull()?.value?: + document.selectFirst("div.meta > div > img")?.attr("src") val trailerurl = @@ -190,4 +191,4 @@ class FilmpertuttiProvider : MainAPI() { } return true } -} \ No newline at end of file +} diff --git a/GuardaSerieProvider/build.gradle.kts b/GuardaSerieProvider/build.gradle.kts new file mode 100644 index 0000000..08e65ec --- /dev/null +++ b/GuardaSerieProvider/build.gradle.kts @@ -0,0 +1,26 @@ +// use an integer for version numbers +version = 3 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Adippe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=guardaserie.golf&sz=%size%" +} \ No newline at end of file diff --git a/GuardaSerieProvider/src/main/AndroidManifest.xml b/GuardaSerieProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/GuardaSerieProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProvider.kt b/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProvider.kt new file mode 100644 index 0000000..e153cd6 --- /dev/null +++ b/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProvider.kt @@ -0,0 +1,124 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.loadExtractor + + +class GuardaSerieProvider : MainAPI() { + override var lang = "it" + override var mainUrl = "https://guardaserie.app" + override var name = "GuardaSerie" + override val hasMainPage = true + override val hasChromecastSupport = true + override var sequentialMainPage = true + override val supportedTypes = setOf( + TvType.TvSeries, + ) + override val mainPage = mainPageOf( + Pair("$mainUrl/serietv-popolari/page/", "Serie Tv Popolari"), + Pair("$mainUrl/serietv-streaming/page/", "Ultime Serie Tv"), + Pair("$mainUrl/top-imdb/page/", "Top IMDB") + ) + + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val url = request.data + page + val soup = app.get(url).document + val home = soup.select("div.mlnew").drop(1).map { series -> + val title = series.selectFirst("div.mlnh-2")!!.text() + val link = series.selectFirst("div.mlnh-2 > h2 > a")!!.attr("href") + val posterUrl = fixUrl(series.selectFirst("img")!!.attr("src")).replace("/60x85-0-85/", "/141x200-0-85/") + + newTvSeriesSearchResponse( + title, + link, + TvType.TvSeries + ) { + this.posterUrl = posterUrl + this.posterHeaders = mapOf("user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") + } + + } + return newHomePageResponse(request.name, home) + } + + override suspend fun search(query: String): List { + val doc = app.post( + mainUrl, data = mapOf( + "do" to "search", + "subaction" to "search", + "story" to query + ) + ).document + return doc.select("div.mlnew").drop(1).map { series -> + val title = series.selectFirst("div.mlnh-2")!!.text() + val link = series.selectFirst("div.mlnh-2 > h2 > a")!!.attr("href") + val posterUrl = fixUrl(series.selectFirst("img")!!.attr("src")).replace("/60x85-0-85/", "/141x200-0-85/") + newMovieSearchResponse( + title, + link, + TvType.Movie + ) { + this.posterUrl = posterUrl + this.posterHeaders = mapOf("user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") + } + + } + } + + override suspend fun load(url: String): LoadResponse { + val document = app.get(url).document + val title = document.selectFirst("h1")!!.text().removeSuffix(" streaming") + val description = document.selectFirst("div.tv_info_right")?.textNodes()?.joinToString("")?.removeSuffix("!")?.trim() + val rating = document.selectFirst("span.post-ratings")?.text() + var year = document.select("div.tv_info_list > ul").find { it.text().contains("Anno") }?.text()?.substringBefore("-")?.filter { it.isDigit() }?.toIntOrNull() + val poster = Regex("poster: '(.*)'").find(document.html())?.groups?.lastOrNull()?.value?.let { + fixUrl( it ) + }?: fixUrl(document.selectFirst("#cover")!!.attr("src")) + + val episodeList = document.select("div.tab-content > div").mapIndexed { season, data -> + data.select("li").mapIndexed { epNum, epData -> + val epName = epData.selectFirst("a")?.attr("data-title") + val data = epData.select("div.mirrors > a").map { it.attr("data-link") } + .joinToString ( "," ) + Episode( + data = data, + name = epName, + season = season + 1, + episode = epNum + 1, + ) + } + }.flatten() + + return newTvSeriesLoadResponse( + title, + url, + TvType.TvSeries, + episodeList + ) { + addRating(rating) + this.plot = description + this.year = year + this.posterUrl = poster + this.posterHeaders = mapOf("user-agent" to "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val links = data.split(",") + links.map { url -> + loadExtractor(fixUrl(url), fixUrl(url), subtitleCallback, callback) + } + return true + } +} diff --git a/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProviderPlugin.kt b/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProviderPlugin.kt new file mode 100644 index 0000000..c0a0708 --- /dev/null +++ b/GuardaSerieProvider/src/main/kotlin/com/lagradost/GuardaSerieProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class GuardaSerieProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(GuardaSerieProvider()) + } +} \ No newline at end of file diff --git a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt index 591db74..463982f 100644 --- a/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt +++ b/HDMovie5/src/main/kotlin/com/lagradost/HDMovie5.kt @@ -9,7 +9,7 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Element class HDMovie5 : MainAPI() { - override var mainUrl = "https://hdmovie2.rip" + override var mainUrl = "https://hdmovie2.skin" override var name = "HDMovie" override var lang = "hi" diff --git a/IlGenioDelloStreamingProvider/build.gradle.kts b/IlGenioDelloStreamingProvider/build.gradle.kts index 112f2e5..fde0550 100644 --- a/IlGenioDelloStreamingProvider/build.gradle.kts +++ b/IlGenioDelloStreamingProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 5 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe","Forthe") /** * Status int as the following: @@ -18,10 +18,9 @@ cloudstream { * */ status = 1 // will be 3 if unspecified tvTypes = listOf( - "TvSeries}", "TvSeries", "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=ilgeniodellostreaming.quest&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=ilgeniodellostreaming.hair&sz=%size%" } \ No newline at end of file diff --git a/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt b/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt index b747ab4..93be456 100644 --- a/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt +++ b/IlGenioDelloStreamingProvider/src/main/kotlin/com/lagradost/IlGenioDelloStreamingProvider.kt @@ -1,195 +1,250 @@ package com.lagradost +import android.util.Log import com.lagradost.cloudstream3.* -import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.LoadResponse.Companion.addRating +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.network.CloudflareKiller +import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson -import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson import com.lagradost.cloudstream3.utils.ExtractorLink -import com.lagradost.cloudstream3.utils.ShortLink +import com.lagradost.cloudstream3.utils.ShortLink.unshorten import com.lagradost.cloudstream3.utils.loadExtractor -import org.jsoup.Jsoup import org.jsoup.nodes.Element - class IlGenioDelloStreamingProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://ilgeniodellostreaming.quest" + override var mainUrl = "https://ilgeniodellostreaming.hair" override var name = "IlGenioDelloStreaming" override val hasMainPage = true override val hasChromecastSupport = true - override val supportedTypes = setOf( - TvType.Movie, - TvType.TvSeries, - ) - override val mainPage = mainPageOf( - Pair("$mainUrl/category/film/page/", "Film Popolari"), - Pair("$mainUrl/category/serie-tv/page/", "Serie Tv Popolari"), - Pair("$mainUrl/the-most-voted/page/", "I più votati"), - Pair("$mainUrl/prime-visioni/page/", "Ultime uscite"), - ) + override var sequentialMainPage = true + override val supportedTypes = + setOf( + TvType.Movie, + TvType.TvSeries, + ) + override val mainPage = + mainPageOf( + Pair("$mainUrl/popular-movies/page/", "Film Popolari"), + Pair("$mainUrl/the-most-voted/page/", "I più votati"), + ) + private val interceptor = CloudflareKiller() - override suspend fun getMainPage( - page: Int, - request: MainPageRequest - ): HomePageResponse { + private fun fixTitle(element: Element?): String { + return element?.text() + ?.trim() + ?.substringBefore("Streaming") + ?.replace("[HD]", "") + ?.replace("\\(\\d{4}\\)".toRegex(), "") + ?: "No Title found" + } + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { val url = request.data + page - val soup = app.get(url).document - val home = soup.select("div.items > article.item").map { - val title = it.selectFirst("div.data > h3 > a")!!.text().substringBeforeLast("(").substringBeforeLast("[") - val link = it.selectFirst("div.poster > a")!!.attr("href") - val quality = getQualityFromString(it.selectFirst("span.quality")?.text()) - TvSeriesSearchResponse( - title, - link, - this.name, - TvType.Movie, - it.selectFirst("img")!!.attr("src"), - null, - null, - quality = quality - ) + + val soup = app.get(url, referer = mainUrl).document + val home = soup.select("div.items > article.item").mapNotNull { it.toMainPageResult() } + val hasNext = home.isNotEmpty() + return newHomePageResponse(arrayListOf(HomePageList(request.name, home)), hasNext = hasNext) + } + + private fun Element.toMainPageResult(): SearchResponse { + val title = + fixTitle(this.selectFirst("div.data>h3")) + val isMovie = + (this.selectFirst("div.data>h3")?.text() ?: "").contains("\\(\\d{4}\\)".toRegex()) + val link = + this.selectFirst("div.poster>a")?.attr("href") + ?: throw ErrorLoadingException("No Link found") + + val quality = this.selectFirst("div.poster>span.quality")?.text() + val posterUrl = this.selectFirst("div.poster>a>img")?.attr("src") + + return if (isMovie) { + newMovieSearchResponse(title, link, TvType.Movie) { + addPoster(posterUrl) + quality?.let { addQuality(it) } + } + } else { + newTvSeriesSearchResponse(title, link, TvType.TvSeries) { + addPoster(posterUrl) + quality?.let { addQuality(it) } + } } - return newHomePageResponse(request.name, home) } override suspend fun search(query: String): List { - val queryformatted = query.replace(" ", "+") - val url = "$mainUrl?s=$queryformatted" - val doc = app.get(url,referer= mainUrl ).document - return doc.select("div.result-item").map { - val href = it.selectFirst("div.image > div > a")!!.attr("href") - val poster = it.selectFirst("div.image > div > a > img")!!.attr("src") - val name = it.selectFirst("div.details > div.title > a")!!.text().substringBeforeLast("(").substringBeforeLast("[") - MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - poster - ) + val queryFormatted = query.replace(" ", "+") + val url = "$mainUrl/?s=$queryFormatted" + val doc = app.get(url, referer = mainUrl, interceptor = interceptor).document + return doc.select("div.search-page>div.result-item").map { it.toSearchResult() } + } + private fun Element.toSearchResult(): SearchResponse { + val title = + fixTitle(this.selectFirst("div.title>a")) + val isMovie = + (this.selectFirst("div.title>a")?.text() ?: "").contains("\\(\\d{4}\\)".toRegex()) + val link = + this.selectFirst("div.title>a")?.attr("href") + ?: throw ErrorLoadingException("No Link found") + + val quality = + this.selectFirst("div.title>a")?.text()?.substringAfter("[")?.substringBefore("]") + val posterUrl = this.selectFirst("a>img")?.attr("src") + + return if (isMovie) { + newMovieSearchResponse(title, link, TvType.Movie) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } else { + newTvSeriesSearchResponse(title, link, TvType.TvSeries) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } } } override suspend fun load(url: String): LoadResponse { - val page = app.get(url) - val document = page.document - val type = if (document.select("div.seasons-wraper").isNotEmpty()){TvType.TvSeries} else{TvType.Movie} - val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(").substringBefore("[") - val description = document.selectFirst("div#info")?.selectFirst("p")?.html() - val rating = document.select("span.valor").last()?.text()?.split(" ")?.get(0) - var year = document.selectFirst(" div.data > div.extra > span.date")!!.text().substringAfter(",") - .filter { it.isDigit() } - if (year.length > 4) { - year = year.dropLast(4) - } - - val poster = document.selectFirst("div.poster > img")!!.attr("src") - - val recomm = document.select("article.w_item_b").map { - val href = it.selectFirst("a")!!.attr("href") - val posterUrl = it.selectFirst("img")!!.attr("src") - val name = it.selectFirst("div.data > h3")!!.text().substringBeforeLast("(").substringBeforeLast("[") - MovieSearchResponse( - name, - href, - this.name, - TvType.Movie, - posterUrl - ) - - } - + val document = app.get(url, referer = mainUrl).document + val type = + if (document.select("div.seasons-wraper").isNotEmpty()) TvType.TvSeries + else TvType.Movie + val title = + fixTitle(document.selectFirst("div.data > h1")) + val description = + document.selectFirst("div#info") + ?.text() + ?.substringAfter(".") + ?.substringBefore("Leggi anche") + val year = + document.selectFirst("b.variante>strong>a") + ?.text() + ?.substringBefore("-") + ?.substringAfter(",") + ?.filter { it.isDigit() } + val poster = document.selectFirst("div.poster>img")?.attr("src") + val rating = document.selectFirst("span.valor>strong")?.text()?.toRatingInt() + val trailer = + document.selectFirst("img.youtube__img") + ?.attr("src") + ?.substringAfter("vi/") + ?.substringBefore("/") + ?.let { "https://www.youtube.com/watch?v=$it" } + val recomm = document.select("article.w_item_b").map { it.toRecommendResult() } if (type == TvType.TvSeries) { - - val episodeList = ArrayList() - document.selectFirst("div.seasons-wraper") - ?.select("div.accordion-item ")?.groupBy {it.selectFirst("span.season-title")!!.text() }?.map { seasons -> - seasons.value.map {season -> season.select("div.episode-wrap")}.flatten() - .groupBy { it.selectFirst("li.season-no")?.text()?.substringBeforeLast(" ") } - .map { episodeItaSub -> - val episodes = episodeItaSub.value - val posterUrl = episodes.firstNotNullOf { it.selectFirst("img")?.attr("src")} - val epName = episodes.firstNotNullOf{it.selectFirst("li.other_link")?.text()?:""} - - episodes.map{ episode -> - val seasonNo = episode.selectFirst("li.season-no") - val subtag = seasonNo?.text()?.takeIf {it.contains("Sub")}?.substringAfter(" ") ?: "" - val urls = episode.getElementsByAttributeValue("target", "_blank").map { it.attr("href").trim() } - .filter { it.isNotEmpty()}.toJson() - episodeList.add(Episode( - data = urls, - posterUrl = posterUrl, - season = seasons.key.toIntOrNull(), - name = "$epName ${subtag.uppercase()}", - episode = seasonNo?.text()?.substringAfter("x")?.filter { it.isDigit() }?.toIntOrNull() - - )) - } - - - + val episodeList = + document.select("div.accordion>div.accordion-item") + .map { element -> + val season = + element.selectFirst("li.s_title>span.season-title") + ?.text() + ?.toIntOrNull() + ?: 0 + element.select("div.episode-wrap").map { episode -> + episode.toEpisode(season) } - } + } + .flatten() - - val seasonnames = document.selectFirst("div#info")?.select("p")?.map {it.children() } - ?.filter { it.size<3 && it.isNotEmpty()}?.map{it.text()} - - return newTvSeriesLoadResponse( - title, - url, - type, - episodeList - ){ - addRating(rating) + return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { + this.year = year?.toIntOrNull() this.plot = description - this.year = year.toIntOrNull() - this.posterUrl = poster this.recommendations = recomm - this.seasonNames = seasonnames!!.mapIndexed { index, s -> SeasonData(index, s) } - + addPoster(poster) + addRating(rating) + addTrailer(trailer) } - - } else { val actors: List = - document.select("div.cast_wraper > ul > li").map { actorData -> - val actorName = actorData.children()[1].text() - val actorImage : String? = actorData.selectFirst("img")?.attr("data-src") - val roleActor = actorData.children()[2].text() - ActorData(actor = Actor(actorName, image = actorImage), roleString = roleActor ) + document.select("div.cast_wraper>ul>li").map { actordata -> + val actorName = actordata.selectFirst("strong")?.text() ?: "" + val actorImage: String = + actordata.selectFirst("figure>img")?.attr("src") ?: "" + ActorData(actor = Actor(actorName, image = actorImage)) } - return newMovieLoadResponse( - title, - url, - type, - (document.select("div.embed-player") + document.select("a.link_a")).map { (it.attr("href") + it.attr("data-id")).trim() }.distinct().toJson(), - ) { - posterUrl = fixUrlNull(poster) - this.year = year.toIntOrNull() + val data = document.select(".embed-player").map { it.attr("data-id") }.toJson() + return newMovieLoadResponse(title, data, TvType.Movie, data) { + this.year = year?.toIntOrNull() this.plot = description - addRating(rating) - this.recommendations = recomm - this.duration = null this.actors = actors + this.recommendations = recomm + addPoster(poster) + addRating(rating) + addTrailer(trailer) } } } + private fun Element.toRecommendResult(): SearchResponse { + val title = + fixTitle(this.selectFirst("div.data>h3")) + val isMovie = + (this.selectFirst("div.data>h3")?.text() ?: "").contains("\\(\\d{4}\\)".toRegex()) + val link = + this.selectFirst("a")?.attr("href") ?: throw ErrorLoadingException("No Link found") + + val quality = + this.selectFirst("div.data>h3")?.text()?.substringAfter("[")?.substringBefore("]") + val posterUrl = this.selectFirst("div.image>img")?.attr("src") + + return if (isMovie) { + newMovieSearchResponse(title, link, TvType.Movie) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } else { + newTvSeriesSearchResponse(title, link, TvType.TvSeries) { + addPoster(posterUrl) + if (quality != null) { + addQuality(quality) + } + } + } + } + + private fun Element.toEpisode(season: Int): Episode { + val data = + this.select("div.fix-table>table>tbody>tr>td>a[target=_blank]") // buckler.link + .map { it.attr("href") } + .toJson() + val epNum = + this.selectFirst("li.season-no") + ?.text() + ?.substringAfter("x") + ?.substringBefore(" ") + ?.filter { it.isDigit() } + .orEmpty().ifBlank { "0" } + + val epTitle = + this.selectFirst("li.other_link>a")?.text().orEmpty().ifBlank { + "Episodio $epNum" + } + val posterUrl = this.selectFirst("img")?.attr("src") + return Episode(data, epTitle, season, epNum?.toInt(), posterUrl = posterUrl) + } + override suspend fun loadLinks( data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { - val links = tryParseJson>(data) - links?.map { link -> - val url = ShortLink.unshorten(link).replace("/v/", "/e/").replace("/f/", "/e/") - loadExtractor(url, data, subtitleCallback, callback) + parseJson>(data).map { videoUrl -> + loadExtractor(unshorten(videoUrl), data, subtitleCallback, callback) } return true } -} \ No newline at end of file +} diff --git a/MundoDonghuaProvider/build.gradle.kts b/MundoDonghuaProvider/build.gradle.kts index 62eb407..9f336ab 100644 --- a/MundoDonghuaProvider/build.gradle.kts +++ b/MundoDonghuaProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -23,4 +23,4 @@ cloudstream { ) iconUrl = "https://www.google.com/s2/favicons?domain=www.mundodonghua.com&sz=%size%" -} \ No newline at end of file +} diff --git a/MundoDonghuaProvider/src/main/kotlin/com/lagradost/MundoDonghuaProvider.kt b/MundoDonghuaProvider/src/main/kotlin/com/lagradost/MundoDonghuaProvider.kt index 8381b2f..e41d77e 100644 --- a/MundoDonghuaProvider/src/main/kotlin/com/lagradost/MundoDonghuaProvider.kt +++ b/MundoDonghuaProvider/src/main/kotlin/com/lagradost/MundoDonghuaProvider.kt @@ -36,9 +36,10 @@ class MundoDonghuaProvider : MainAPI() { val title = it.selectFirst("h5")?.text() ?: "" val poster = it.selectFirst(".fit-1 img")?.attr("src") val epRegex = Regex("(\\/(\\d+)\$)") - val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")?.replace("/ver/","/donghua/") val epnumRegex = Regex("((\\d+)$)") val epNum = epnumRegex.find(title)?.value?.toIntOrNull() + val epNumRemoveRegex = Regex("/" + epNum.toString() + "/.*") + val url = it.selectFirst("a")?.attr("href")?.replace(epRegex,"")?.replace("/ver/","/donghua/")?.replace(epNumRemoveRegex,"") val dubstat = if (title.contains("Latino") || title.contains("Castellano")) DubStatus.Dubbed else DubStatus.Subbed newAnimeSearchResponse(title.replace(Regex("Episodio|(\\d+)"),"").trim(), fixUrl(url ?: "")) { this.posterUrl = fixUrl(poster ?: "") @@ -214,4 +215,4 @@ class MundoDonghuaProvider : MainAPI() { } return true } -} \ No newline at end of file +} diff --git a/NineGoalProvider/build.gradle.kts b/NineGoalProvider/build.gradle.kts new file mode 100644 index 0000000..bf5b4e9 --- /dev/null +++ b/NineGoalProvider/build.gradle.kts @@ -0,0 +1,12 @@ +version = 6 + +cloudstream { + description = "" + authors = listOf( "ImZaw" ) + + status = 1 + + tvTypes = listOf( "Live" ) + + iconUrl = "https://media.discordapp.net/attachments/1027568249900109875/1046110428402561025/JK8J1KX.png?width=%size%&height=%size%" +} diff --git a/NineGoalProvider/src/main/AndroidManifest.xml b/NineGoalProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..263556b --- /dev/null +++ b/NineGoalProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalPlugin.kt b/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalPlugin.kt new file mode 100644 index 0000000..c9a5e9e --- /dev/null +++ b/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalPlugin.kt @@ -0,0 +1,12 @@ +package com.ninegoal + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class NineGoalPlugin: Plugin() { + override fun load(context: Context) { + registerMainAPI(NineGoal()) + } +} \ No newline at end of file diff --git a/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalProvider.kt b/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalProvider.kt new file mode 100644 index 0000000..aa444e4 --- /dev/null +++ b/NineGoalProvider/src/main/kotlin/com/ninegoal/9GoalProvider.kt @@ -0,0 +1,179 @@ +package com.ninegoal + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.Qualities +import java.net.URL +import java.net.UnknownHostException +import java.util.* + +data class Data ( + @JsonProperty("id" ) var id : String? = null, + @JsonProperty("name" ) var name : String? = null, + @JsonProperty("slug" ) var slug : String? = null, + @JsonProperty("home" ) var home : Home? = Home(), + @JsonProperty("away" ) var away : Away? = Away(), + @JsonProperty("scores" ) var scores : Scores? = Scores(), + @JsonProperty("is_live" ) var isLive : Boolean? = null +) +data class Home ( + @JsonProperty("model_id" ) var modelId : String? = null, + @JsonProperty("name" ) var name : String? = null, + @JsonProperty("slug" ) var slug : String? = null, + @JsonProperty("logo" ) var logo : String? = null +) +data class Away ( + @JsonProperty("model_id" ) var modelId : String? = null, + @JsonProperty("name" ) var name : String? = null, + @JsonProperty("slug" ) var slug : String? = null, + @JsonProperty("logo" ) var logo : String? = null +) +data class Scores ( + @JsonProperty("home" ) var home : Int? = null, + @JsonProperty("away" ) var away : Int? = null +) +data class matchesJSON ( + @JsonProperty("data" ) var data : ArrayList = arrayListOf() +) +data class oneMatch ( + @JsonProperty("data" ) var data : Data? = Data() +) + + +data class PlayUrls ( + @JsonProperty("name" ) var name : String? = null, + @JsonProperty("cdn" ) var cdn : String? = null, + @JsonProperty("slug" ) var slug : String? = null, + @JsonProperty("url" ) var url : String? = null, + @JsonProperty("role" ) var role : String? = null +) +data class sourceData ( + @JsonProperty("id" ) var id : String? = null, + @JsonProperty("name" ) var name : String? = null, + @JsonProperty("slug" ) var slug : String? = null, + @JsonProperty("has_lineup" ) var hasLineup : Boolean? = null, + @JsonProperty("has_tracker" ) var hasTracker : Boolean? = null, + @JsonProperty("play_urls" ) var playUrls : ArrayList = arrayListOf() +) +data class sourcesJSON ( + @JsonProperty("data" ) var data : sourceData? = sourceData() +) + +class NineGoal : MainAPI() { + override var mainUrl = "https://9goaltv.to" + override var name = "9Goal" + override var lang = "en" + override val hasDownloadSupport = false + override val hasMainPage = true + override val supportedTypes = setOf( + TvType.Live + ) + + override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse { + val doc = app.get(mainUrl).document + val apiUrl = doc.select("head > script") + .firstOrNull { it.html().contains("window.api_base_url") }.let { + Regex("""window\.api_base_url = "(.*)";""").find(it?.html() ?: "")?.groupValues?.lastOrNull() + } ?: "https://justameanlessdomain.com" + val matchesData = parseJson(app.get("$apiUrl/v1/match/featured").text) + val liveHomePageList = matchesData.data.filter { it.isLive == true }.map { + LiveSearchResponse( + it.name ?: "", + apiUrl + "/v1/match/" + it.id, + this@NineGoal.name, + TvType.Live, + "https://img.zr5.repl.co/vs?title=${it.name}&home=${it.home?.logo}&away=${it.away?.logo}&live=true", + ) + } + val featuredHomePageList = matchesData.data.filter { it.isLive == false }.map { + LiveSearchResponse( + it.name ?: "", + apiUrl + "/v1/match/" + it.id, + this@NineGoal.name, + TvType.Live, + "https://img.zr5.repl.co/vs?title=${it.name}&home=${it.home?.logo}&away=${it.away?.logo}", + ) + } + return HomePageResponse( + arrayListOf( + HomePageList("Live", liveHomePageList, isHorizontalImages = true), + HomePageList("Featured", featuredHomePageList, isHorizontalImages = true) + ) + ) + } + + override suspend fun load(url: String): LoadResponse { + val json = parseJson(app.get(url).text).data + return LiveStreamLoadResponse( + json?.name ?: "", + url, + this.name, + "$url/stream", + "https://img.zr5.repl.co/vs?title=${json?.name}&home=${json?.home?.logo}&away=${json?.away?.logo}&live=${json?.isLive}" + ) + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + val sourcesData = parseJson(app.get(data).text).data + sourcesData?.playUrls?.apmap { + val quality = it.name?.substringAfter("(")?.substringBefore(")").let { qualityText -> + when (qualityText) { + "Full HD" -> 1080 + "HD" -> 720 + "SD" -> 480 + else -> Qualities.Unknown.value + } + } + val language = it.name?.replace("""\(.*""".toRegex(), "") ?: "" + val url = URL(it.url) + val requestStatus = try { + app.head( + fixUrl( + it.url ?: "" + ) + ).isSuccessful + } catch (e: UnknownHostException) { + false + } + if (!requestStatus) { + mapOf( + "(1)" to "playing.smoothlikebutterstream.com", + "(2)" to "playing.tunnelcdnsw.net", + "(3)" to "playing.goforfreedomwme.net", + "(4)" to "turnthe.gameon.tel", + "(5)" to "playing.whydontyoustreamwme.com" + ).apmap { (name, value) -> + callback.invoke( + ExtractorLink( + this.name, + "$language $name", + it.url?.replace(url.host, value) ?: "", + "$mainUrl/", + quality, + isM3u8 = true, + ) + ) + } + } else { + callback.invoke( + ExtractorLink( + this.name, + "$language ${sourcesData.name}", + it.url ?: "", + "$mainUrl/", + quality, + isM3u8 = true, + ) + ) + } + } + return true + } +} diff --git a/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviePediaProvider.kt b/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviePediaProvider.kt index 33e0a30..8ad3e72 100644 --- a/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviePediaProvider.kt +++ b/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviePediaProvider.kt @@ -21,18 +21,16 @@ class PinoyMoviePediaProvider : MainAPI() { val document = app.get(mainUrl).document val mainbody = document.getElementsByTag("body") // All rows will be hardcoded bc of the nature of the site - val rows = listOf( + val rows = listOfNotNull( Pair("Latest Movies", "featured-titles"), Pair("Movies", "dt-movies"), Pair("Digitally Restored", "genre_digitally-restored"), Pair("Action", "genre_action"), Pair("Romance", "genre_romance"), Pair("Comedy", "genre_comedy"), - Pair("Family", "genre_family") - ).toMutableList() - if (settingsForProvider.enableAdult) { - rows.add(Pair("Adult +18", "genre_pinay-sexy-movies")) - } + Pair("Family", "genre_family"), + if (settingsForProvider.enableAdult) Pair("Adult +18", "genre_pinay-sexy-movies") else null + ) rows.forEach { item -> val title = item.first val inner = mainbody?.select("div#${item.second} > article") diff --git a/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviesEsProvider.kt b/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviesEsProvider.kt index 5b7ad69..0644d69 100644 --- a/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviesEsProvider.kt +++ b/PinoyMovies/src/main/kotlin/com/lagradost/PinoyMoviesEsProvider.kt @@ -79,7 +79,7 @@ class PinoyMoviesEsProvider : MainAPI() { ) }?.distinctBy { c -> c.url } ?: listOf() //Add to list of homepages - if (!elements.isNullOrEmpty()) { + if (elements.isNotEmpty()) { all.add( HomePageList( title, elements @@ -106,15 +106,13 @@ class PinoyMoviesEsProvider : MainAPI() { all.addAll(homepage1) } //2nd rows - val listOfRows = listOf( + val listOfRows = listOfNotNull( Pair("Action", "genre_action"), Pair("Comedy", "genre_comedy"), Pair("Romance", "genre_romance"), - Pair("Horror", "genre_horror") - ).toMutableList() - if (settingsForProvider.enableAdult) { - listOfRows.add(Pair("Rated-R", "genre_rated-r")) - } + Pair("Horror", "genre_horror"), + if (settingsForProvider.enableAdult) Pair("Rated-R", "genre_rated-r") else null + ) val homepage2 = getRowElements( mainbody = mainbody, rows = listOfRows, diff --git a/PinoyMoviesHub/build.gradle.kts b/PinoyMoviesHub/build.gradle.kts new file mode 100644 index 0000000..8d5f157 --- /dev/null +++ b/PinoyMoviesHub/build.gradle.kts @@ -0,0 +1,27 @@ +// use an integer for version numbers +version = 3 + + +cloudstream { + language = "tl" + // All of these properties are optional, you can safely remove them + + description = "Pinoy Movies Hub" + authors = listOf("Jace") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Movie", + "TvSeries", + "AsianDrama", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=www.pinoymovieshub.ph&sz=%size%" +} diff --git a/PinoyMoviesHub/src/main/AndroidManifest.xml b/PinoyMoviesHub/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/PinoyMoviesHub/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHub.kt b/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHub.kt new file mode 100644 index 0000000..b3b179d --- /dev/null +++ b/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHub.kt @@ -0,0 +1,364 @@ +package com.lagradost + +import android.util.Log +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.nodes.Document +import org.jsoup.select.Elements +import java.util.Calendar + +class PinoyMoviesHub : MainAPI() { + private val TAG = "DevDebug" + override var name = "Pinoy Movies Hub" + override var mainUrl = "https://pinoymovieshub.ph" + override var lang = "tl" + override val supportedTypes = setOf(TvType.Movie, TvType.TvSeries, TvType.AsianDrama) + override val hasDownloadSupport = true + override val hasMainPage = true + override val hasQuickSearch = false + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val doc = app.get(mainUrl).document + val rows = listOfNotNull( + Pair("Suggestion", "div.items.featured"), + Pair("Pinoy Movies and TV", "div.items.full"), + //Pair("Pinoy Teleserye and TV Series", "tvload"), + Pair("Action", "div#genre_action"), + Pair("Comedy", "div#genre_comedy"), + Pair("Romance", "div#genre_romance"), + Pair("Horror", "div#genre_horror"), + Pair("Drama", "div#genre_drama"), + if (settingsForProvider.enableAdult) Pair("Rated-R 18+", "genre_rated-r") else null + ) + //Log.i(TAG, "Parsing page..") + val maindoc = doc.selectFirst("div.module") + ?.select("div.content.full_width_layout.full") + + val all = rows.mapNotNull { pair -> + // Fetch row title + val title = pair.first + // Fetch list of items and map + //Log.i(TAG, "Title => $title") + val results = maindoc?.select(pair.second)?.select("article").getResults(this.name) + if (results.isEmpty()) { + return@mapNotNull null + } + HomePageList( + name = title, + list = results, + isHorizontalImages = false + ) + } + return HomePageResponse(all) + } + + override suspend fun search(query: String): List { + val searchUrl = "${mainUrl}/?s=${query}" + return app.get(searchUrl).document + .selectFirst("div#archive-content") + ?.select("article") + .getResults(this.name) + } + + override suspend fun load(url: String): LoadResponse { + val apiName = this.name + val doc = app.get(url).document + val body = doc.getElementsByTag("body").firstOrNull() + val sheader = body?.selectFirst("div.sheader") + + //Log.i(TAG, "Result => (url) ${url}") + val poster = sheader?.selectFirst("div.poster > img") + ?.attr("src") + + val title = sheader + ?.selectFirst("div.data > h1") + ?.text() ?: "" + val descript = body?.selectFirst("div#info div.wp-content")?.text() + val year = body?.selectFirst("span.date")?.text()?.trim()?.takeLast(4)?.toIntOrNull() + + //Parse episodes + val episodeList = body?.selectFirst("div#episodes") + ?.select("li") + ?.mapNotNull { + var epCount: Int? = null + var seasCount: Int? = null + val divEp = it?.selectFirst("div.episodiotitle") ?: return@mapNotNull null + val firstA = divEp.selectFirst("a") + + it.selectFirst("div.numerando")?.text() + ?.split("-")?.mapNotNull { seasonEps -> + seasonEps.trim().toIntOrNull() + }?.let { divEpSeason -> + if (divEpSeason.isNotEmpty()) { + if (divEpSeason.size > 1) { + epCount = divEpSeason[1] + seasCount = divEpSeason[0] + } else { + epCount = divEpSeason[0] + } + } + } + + val eplink = firstA?.attr("href") ?: return@mapNotNull null + val imageEl = it.selectFirst("img") + val epPoster = imageEl?.attr("src") ?: imageEl?.attr("data-src") + val date = it.selectFirst("span.date")?.text() + + newEpisode( + data = eplink + ) { + this.name = firstA.text() + this.posterUrl = epPoster + this.episode = epCount + this.season = seasCount + this.addDate(parseDateFromString(date)) + } + } ?: emptyList() + + val dataUrl = doc.getMovieId() ?: throw Exception("Movie Id is Null!") + + if (episodeList.isNotEmpty()) { + return newTvSeriesLoadResponse( + name = title, + url = url, + type = TvType.TvSeries, + episodes = episodeList + ) { + this.apiName = apiName + this.posterUrl = poster + this.year = year + this.plot = descript + } + } + + //Log.i(TAG, "Result => (id) ${id}") + return newMovieLoadResponse( + name = title, + url = url, + dataUrl = dataUrl, + type = TvType.Movie, + ) { + this.apiName = apiName + this.posterUrl = poster + this.year = year + this.plot = descript + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + var movieId = data + //Log.i(TAG, "movieId => $movieId") + //If episode link, fetch movie id first + if (movieId.startsWith(mainUrl)) { + movieId = app.get(data).document.getMovieId() ?: throw Exception("Movie Id is Null!") + } + + val requestLink = "${mainUrl}/wp-admin/admin-ajax.php" + val action = "doo_player_ajax" + val nume = "1" + val type = "movie" + val seriesTvUrl = "https://series.pinoymovies.tv" + + //Log.i(TAG, "Loading ajax request..") + //Log.i(TAG, "movieId => $movieId") + val doc = app.post( + url = requestLink, + referer = mainUrl, + headers = mapOf( + Pair("User-Agent", USER_AGENT), + Pair("Sec-Fetch-Mode", "cors") + ), + data = mapOf( + Pair("action", action), + Pair("post", movieId), + Pair("nume", nume), + Pair("type", type) + ) + ) + + //Log.i(TAG, "Response (${doc.code}) => ${doc.text}") + tryParseJson(doc.text)?.embed_url?.let { streamLink -> + //Log.i(TAG, "Response (streamLink) => $streamLink") + if (streamLink.isNotBlank()) { + //Decrypt links from https://series.pinoymovies.tv/video/135647s1e1?sid=503&t=alt + if (streamLink.startsWith(seriesTvUrl)) { + //Add suffix: '?sid=503&t=alt' to 'series.pinoymovies.tv' links + val linkSuffix = "?sid=503&t=alt" + val newLink = streamLink.replace(linkSuffix, "") + linkSuffix + //Log.i(TAG, "Response (newLink) => $newLink") + app.get(newLink, referer = streamLink).let { packeddoc -> + val packedString = packeddoc.document.select("script").toString() + //Log.i(TAG, "Response (packedString) => $packedString") + + val newString = getAndUnpack(packedString) + //Log.i(TAG, "Response (newString) => $newString") + + val regex = Regex("(?<=playlist:)(.*)(?=autostart)", setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.IGNORE_CASE)) + val newString2 = regex.find(newString)?.groupValues + ?.getOrNull(1)?.trim() + ?.trimEnd(',') + ?.replace("sources:", "\"sources\":") + ?.replace("'", "\"") + //Log.i(TAG, "Response (newString2) => $newString2") + + tryParseJson?>(newString2)?.let { respData -> + respData.forEach outer@{ respDataItem -> + respDataItem?.sources?.forEach inner@{ srclink -> + val link = srclink.file ?: return@inner + callback.invoke( + ExtractorLink( + name = this.name, + source = this.name, + url = link, + quality = getQualityFromName(srclink.label), + referer = seriesTvUrl, + isM3u8 = link.endsWith("m3u8") + ) + ) + } + } + } + } + } else { + loadExtractor( + url = streamLink, + referer = mainUrl, + callback = callback, + subtitleCallback = subtitleCallback + ) + } + return true + } + } + return false + } + + private fun Document.getMovieId(): String? { + return this.selectFirst("link[rel='shortlink']") + ?.attr("href") + ?.trim() + ?.substringAfter("?p=") + } + + private fun Elements?.getResults(apiName: String): List { + return this?.mapNotNull { + val divPoster = it.selectFirst("div.poster") + val divData = it.selectFirst("div.data") + + val firstA = divData?.selectFirst("a") + val link = fixUrlNull(firstA?.attr("href")) ?: return@mapNotNull null + val qualString = divPoster?.select("span.quality")?.text()?.trim() ?: "" + val qual = getQualityFromString(qualString) + var tvtype = if (qualString.equals("TV")) { TvType.TvSeries } else { TvType.Movie } + if (link.replace("$mainUrl/", "").startsWith("tvshow")) { + tvtype = TvType.TvSeries + } + + val name = divData?.selectFirst("a")?.text() ?: "" + val year = divData?.selectFirst("span")?.text() + ?.trim()?.takeLast(4)?.toIntOrNull() + + val imageDiv = divPoster?.selectFirst("img") + var image = imageDiv?.attr("src") + if (image.isNullOrBlank()) { + image = imageDiv?.attr("data-src") + } + + //Log.i(apiName, "Added => $name / $link") + if (tvtype == TvType.TvSeries) { + TvSeriesSearchResponse( + name = name, + url = link, + apiName = apiName, + type = tvtype, + posterUrl = image, + year = year, + quality = qual, + ) + } else { + MovieSearchResponse( + name = name, + url = link, + apiName = apiName, + type = tvtype, + posterUrl = image, + year = year, + quality = qual, + ) + } + } ?: emptyList() + } + + private fun parseDateFromString(text: String?): String? { + if (text.isNullOrBlank()) { + return null + } + var day = "" + var month = "" + var year = "" + val dateSplit = text.trim().split(".") + if (dateSplit.isNotEmpty()) { + if (dateSplit.size > 1) { + val yearday = dateSplit[1].trim() + year = yearday.takeLast(4) + day = yearday.trim().trim(',') + + month = with (dateSplit[0].lowercase()) { + when { + startsWith("jan") -> "01" + startsWith("feb") -> "02" + startsWith("mar") -> "03" + startsWith("apr") -> "04" + startsWith("may") -> "05" + startsWith("jun") -> "06" + startsWith("jul") -> "07" + startsWith("aug") -> "08" + startsWith("sep") -> "09" + startsWith("oct") -> "10" + startsWith("nov") -> "11" + startsWith("dec") -> "12" + else -> "" + } + } + } else { + year = dateSplit[0].trim().takeLast(4) + } + } + if (day.isBlank()) { + day = "01" + } + if (month.isBlank()) { + month = "01" + } + if (year.isBlank()) { + year = Calendar.getInstance().get(Calendar.YEAR).toString() + } + return "$year-$month-$day" + } + + private data class Response( + @JsonProperty("embed_url") val embed_url: String? + ) + + private data class ResponseData( + @JsonProperty("sources") val sources: List?, + @JsonProperty("image") val image: String?, + ) + + private data class ResponseSources( + @JsonProperty("label") val label: String?, + @JsonProperty("type") val type: String?, + @JsonProperty("file") val file: String? + ) +} diff --git a/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHubPlugin.kt b/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHubPlugin.kt new file mode 100644 index 0000000..02f0764 --- /dev/null +++ b/PinoyMoviesHub/src/main/kotlin/com/lagradost/PinoyMoviesHubPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class PinoyMoviesHubPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(PinoyMoviesHub()) + } +} \ No newline at end of file diff --git a/SkillShareProvider/build.gradle.kts b/SkillShareProvider/build.gradle.kts new file mode 100644 index 0000000..7243cae --- /dev/null +++ b/SkillShareProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 5 + + +cloudstream { + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + language= "en" + authors = listOf("Forthe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Others", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=skillshare.com&sz=%size%" +} diff --git a/SkillShareProvider/src/main/AndroidManifest.xml b/SkillShareProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/SkillShareProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProvider.kt b/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProvider.kt new file mode 100644 index 0000000..d9026eb --- /dev/null +++ b/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProvider.kt @@ -0,0 +1,254 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.MainAPI +import com.lagradost.cloudstream3.SearchResponse +import com.lagradost.cloudstream3.TvType +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.nicehttp.RequestBodyTypes +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody + +class SkillShareProvider : MainAPI() { // all providers must be an instance of MainAPI + override var mainUrl = "https://www.skillshare.com" + override var name = "SkillShare" + + private val apiUrl = "https://www.skillshare.com/api/graphql" + private val bypassApiUrl = "https://skillshare.techtanic.xyz/id" + private val bypassApiUrlFallback = "https://skillshare-api.heckernohecking.repl.co" + + override val supportedTypes = setOf(TvType.Others) + override val hasChromecastSupport = true + override var lang = "en" + override val hasMainPage = true + private var cursor = mutableMapOf("SIX_MONTHS_ENGAGEMENT" to "", "ML_TRENDINESS" to "") + + override val mainPage = + mainPageOf( + "SIX_MONTHS_ENGAGEMENT" to "Popular Classes", + "ML_TRENDINESS" to "Trending Classes", + ) + + private suspend fun queryMovieApi(payload: String): String { + val req = payload.toRequestBody(RequestBodyTypes.JSON.toMediaTypeOrNull()) + return app.post(apiUrl, requestBody = req, referer = "$mainUrl/", timeout = 30).text + } + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val sortAttribute = request.data + if (page == 1) //reset the cursor to "" if the first page is requested + cursor[sortAttribute] = "" + val payload= + """ + { + "query":"query GetClassesByType(${'$'}filter: ClassFilters!, ${'$'}pageSize: Int, ${'$'}cursor: String, ${'$'}type: ClassListType!, ${'$'}sortAttribute: ClassListByTypeSortAttribute) { + classListByType(type: ${'$'}type, where: ${'$'}filter, first: ${'$'}pageSize, after: ${'$'}cursor, sortAttribute: ${'$'}sortAttribute) { + nodes { + id + title + url + sku + smallCoverUrl + largeCoverUrl + } + } + }", + "variables":{ + "type":"TRENDING_CLASSES", + "filter":{ + "subCategory":"", + "classLength":[] + }, + "pageSize":30, + "cursor":"${cursor[sortAttribute]}", + "sortAttribute":"$sortAttribute" + }, + "operationName":"GetClassesByType" + } + """.replace(Regex("\n")," ") + + val responseBody = queryMovieApi(payload) + val parsedJson = parseJson(responseBody).data.classListByType.nodes + val home = parsedJson.map { + it.toSearchResult() + } + cursor[sortAttribute] = parsedJson.lastOrNull()?.id ?: "" //set the right cursor for the nextPage to work + return newHomePageResponse( + arrayListOf(HomePageList(request.name, home, isHorizontalImages = true)), + hasNext = home.isNotEmpty(), + ) + } + + override suspend fun search(query: String): List { + val payload = + """ + { + "query":"fragment ClassFields on Class { + id + smallCoverUrl + largeCoverUrl + sku + title + url + } + + query GetClassesQuery(${"$"}query: String!, ${"$"}where: SearchFilters!, ${"$"}after: String!, ${"$"}first: Int!) { + search(query: ${"$"}query, where: ${"$"}where, analyticsTags: [\"src:browser\", \"src:browser:search\"], after: ${"$"}after, first: ${"$"}first) { + edges { + node { + ...ClassFields + } + } + } + }", + "variables":{ + "query":"$query", + "where":{ + "level": + ["ALL_LEVELS","BEGINNER","INTERMEDIATE","ADVANCED"] + }, + "after":"-1", + "first":30 + }, + "operationName":"GetClassesQuery" + } + """.replace(Regex("\n")," ") + + val responseBody = queryMovieApi(payload) + val home = parseJson(responseBody).data.search.edges.map { + it.node.toSearchResult() + } + return home + } + + private fun ApiNode.toSearchResult(): SearchResponse { + val title = this.title ?: "" + val posterUrl = this.smallCoverUrl + return newTvSeriesSearchResponse( + title, + Data( + title = this.title, + courseId = this.courseId, + largeCoverUrl = this.largeCoverUrl + ).toJson(), + TvType.TvSeries + ) { + addPoster(posterUrl) + } + } + + + override suspend fun load(url: String): LoadResponse? { + val data = parseJson(url) + val document = app.get(bypassApiUrl + "/${data.courseId}") + .parsedSafe() ?: app.get(bypassApiUrlFallback + "/${data.courseId}/0") + .parsedSafe() ?: throw ErrorLoadingException("Invalid Json Response") + val title = data.title ?: "" + val poster = data.largeCoverUrl + val episodeList = document.lessons.mapIndexed { index, episode -> + Episode(episode.url ?: "", episode.title, 1, index) + } + + return newTvSeriesLoadResponse(title, url, TvType.TvSeries, episodeList) { + addPoster(poster) + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + callback.invoke( + ExtractorLink( + name, + name, + data, + isM3u8 = false, + referer = "$mainUrl/", + quality = Qualities.Unknown.value + ) + ) + return true + } + + + data class ApiNode( + //mainpage and search page + @JsonProperty("id") var id: String? = null, + @JsonProperty("title") var title: String? = null, + @JsonProperty("url") var url: String? = null, + @JsonProperty("sku") var courseId: String? = null, + @JsonProperty("smallCoverUrl") var smallCoverUrl: String? = null, + @JsonProperty("largeCoverUrl") var largeCoverUrl: String? = null, + ) + + data class ApiNodes( //mainpage + + @JsonProperty("nodes") var nodes: ArrayList = arrayListOf() + + ) + + data class ApiClassListByType( //mainpage + + @JsonProperty("classListByType") var classListByType: ApiNodes = ApiNodes() + + ) + + data class ApiData( //mainpage + + @JsonProperty("data") var data: ApiClassListByType = ApiClassListByType() + + ) + + data class SearchApiNodes( //search + + @JsonProperty("node") var node: ApiNode = ApiNode() + + ) + + data class SearchApiEdges( //search + + @JsonProperty("edges") var edges: ArrayList = arrayListOf() + + ) + + data class SearchApiSearch( //search + + @JsonProperty("search") var search: SearchApiEdges = SearchApiEdges() + + ) + + data class SearchApiData( //search + + @JsonProperty("data") var data: SearchApiSearch = SearchApiSearch() + + ) + + data class BypassApiLesson( //bypass + + @JsonProperty("title") var title: String? = null, + @JsonProperty("url") var url: String? = null + + ) + + data class BypassApiData( //bypass + + @JsonProperty("class") var title: String? = null, + @JsonProperty("class_thumbnail") var largeCoverUrl: String? = null, + @JsonProperty("lessons") var lessons: ArrayList = arrayListOf() + + ) + + data class Data( + //for loading + val title: String? = null, + val courseId: String? = null, + val largeCoverUrl: String? = null, + ) +} diff --git a/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProviderPlugin.kt b/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProviderPlugin.kt new file mode 100644 index 0000000..2bc844e --- /dev/null +++ b/SkillShareProvider/src/main/kotlin/com/lagradost/SkillShareProviderPlugin.kt @@ -0,0 +1,14 @@ +package com.lagradost + +import android.content.Context +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin + +@CloudstreamPlugin +class SkillShareProviderPlugin : Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list + // directly. + registerMainAPI(SkillShareProvider()) + } +} diff --git a/SoraItalianStream/build.gradle.kts b/SoraItalianStream/build.gradle.kts new file mode 100644 index 0000000..0356d7f --- /dev/null +++ b/SoraItalianStream/build.gradle.kts @@ -0,0 +1,29 @@ +// use an integer for version numbers +version = 2 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + description = "Provider che utilizza tmdb. Non tutti i links sono funzionanti" + authors = listOf("Adippe", "Forthe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "TvSeries", + "Movie", + "AnimeMovie", + "Anime", + "OVA" + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=seriesflix.video&sz=%size%" +} \ No newline at end of file diff --git a/SoraItalianStream/src/main/AndroidManifest.xml b/SoraItalianStream/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0474547 --- /dev/null +++ b/SoraItalianStream/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt b/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt new file mode 100644 index 0000000..b277fb3 --- /dev/null +++ b/SoraItalianStream/src/main/kotlin/SoraItalianExtractor.kt @@ -0,0 +1,372 @@ +package com.lagradost + +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import com.lagradost.cloudstream3.utils.ShortLink.unshorten +import com.lagradost.nicehttp.Requests + +object SoraItalianExtractor : SoraItalianStream() { + + suspend fun invoGuardare( + id: String? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val res = app.get( + "https://guardahd.stream/movie/$id", + referer = "/" + ).document + res.select("ul._player-mirrors > li").forEach { source -> + loadExtractor( + fixUrl(source.attr("data-link")), + "https://guardahd.stream", + subtitleCallback, + callback + ) + } + } + + suspend fun invoGuardaserie( + id: String? = null, + season: Int? = null, + episode: Int? = null, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val url = app.post( + guardaserieUrl, data = mapOf( + "do" to "search", + "subaction" to "search", + "story" to id!! + ) + ).document.selectFirst("h2>a")?.attr("href") ?: return + + val document = app.get(url).document + document.select("div.tab-content > div").forEachIndexed { seasonData, data -> + data.select("li").forEachIndexed { epNum, epData -> + if (season == seasonData + 1 && episode == epNum + 1) { + epData.select("div.mirrors > a.mr").forEach { + loadExtractor( + fixUrl(it.attr("data-link")), + guardaserieUrl, + subtitleCallback, + callback + ) + } + } + } + } + } + + suspend fun invoFilmpertutti( + id: String?, + title: String?, + type: String?, + season: Int?, + episode: Int?, + year: Int?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + val url = when (type) { + "movie" -> "$filmpertuttiUrl/search/$title%20$year/feed/rss2" + else -> "$filmpertuttiUrl/search/$title/feed/rss2" + } + val res = app.get(url).text + val links = Regex("(.*)").findAll(res).map { it.groupValues.last() }.toList() + .filter { it != filmpertuttiUrl } + links.apmap { + val doc = app.get(it).document + if (id == doc.selectFirst("div.rating > p > a") //check if corresponds to the imdb id + ?.attr("href") + ?.substringAfterLast("/") + ) { + if (type == "tv") { + + val seasonData = doc.select("div.accordion-item").filter { a -> + a.selectFirst("#season > ul > li.s_title > span")!! + .text() + .isNotEmpty() + }.find { + season == it.selectFirst("#season > ul > li.s_title > span")!! + .text() + .toInt() + } + + val episodeData = seasonData?.select("div.episode-wrap")?.find { + episode == it.selectFirst("li.season-no")!!.text() + .substringAfter("x") + .substringBefore(" ") + .filter { it.isDigit() }.toIntOrNull() + } + + episodeData?.select("#links > div > div > table > tbody:nth-child(2) > tr") + ?.forEach { + val unshortenLink = unshorten(it.selectFirst("a")?.attr("href") ?: "") + loadExtractor( + unshortenLink, + filmpertuttiUrl, + subtitleCallback, + callback + ) + } + } else { //movie + doc.select("#info > ul > li").forEach { + val unshortenLink = unshorten(it.selectFirst("a")?.attr("href") ?: "") + loadExtractor( + unshortenLink, + filmpertuttiUrl, + subtitleCallback, + callback + ) + } + } + } + } + } + + suspend fun invoCb01( + title: String?, + year: Int?, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ) { + + val res = app.get("$cb01Url/search/$title%20$year/feed").text + val links = Regex("(.*)").findAll(res).map { it.groupValues.last() }.toList() + .filter { it != cb01Url && it != "$cb01Url/" } + if (links.size != 1) return + + links.apmap { + val doc = app.get(it).document + doc.select("tr > td > a[href*='stayonline.pro']").forEach { + val link = it.selectFirst("a")?.attr("href") ?: "" + val apiPostId = link.substringAfter("/l/") + .substringBefore("/") //https://stayonline.pro/l/abcdef/ -> abcdef + val apiBodyBypass = app.post( + "https://stayonline.pro/ajax/linkView.php", + data = mapOf("id" to apiPostId) + ).text + var bypassedUrl = + apiBodyBypass.substringAfter("\"value\": \"").substringBefore("\"") + .replace("\\", "") //bypass stayonline link + + if (bypassedUrl.contains("mixdrop.club")) //https://mixdrop.club/f/lllllllll/2/abcdefghilmn.mp4 (fake mp4 url) -> https://mixdrop.ch/e/lllllllll + bypassedUrl = bypassedUrl.replace("mixdrop.club", "mixdrop.ch") + .substringBeforeLast("/") + .substringBeforeLast("/") + .replace("/f/", "/e/") + else + bypassedUrl = unshorten(bypassedUrl) + loadExtractor( + bypassedUrl, + cb01Url, + subtitleCallback, + callback + ) + } + + } + } + + suspend fun invoAnimeWorld( + malId: String?, + title: String?, + episode: Int?, + callback: (ExtractorLink) -> Unit + ) { + val pagedata = app.get("$animeworldUrl/search?keyword=$title").document + + pagedata.select(".film-list > .item").map { + fixUrl(it.select("a.name").firstOrNull()?.attr("href") ?: "", animeworldUrl) + }.apmap { + val document = app.get(it).document + val pageMalId = document.select("#mal-button").attr("href") + .split('/').last().toString() + if (malId == pageMalId) { + val servers = document.select(".widget.servers") + servers.select(".server[data-name=\"9\"] .episode > a").toList() + .filter { it.attr("data-episode-num").toIntOrNull()?.equals(episode) ?: false } + .forEach { id -> + val url = tryParseJson( + app.get("$animeworldUrl/api/episode/info?id=${id.attr("data-id")}").text + )?.grabber + var dub = false + for (meta in document.select(".meta dt, .meta dd")) { + val text = meta.text() + if (text.contains("Audio")) { + dub = meta.nextElementSibling()?.text() == "Italiano" + } + } + val nameData = if (dub) { + "AnimeWorld DUB" + } else { + "AnimeWorld SUB" + } + + callback.invoke( + ExtractorLink( + "AnimeWorld", + nameData, + url ?: "", + referer = animeworldUrl, + quality = Qualities.Unknown.value + ) + ) + } + } + } + } + + suspend fun invoAniPlay( + malId: String?, + title: String?, + episode: Int?, + year: Int?, + callback: (ExtractorLink) -> Unit + ) { + val response = + parseJson>(app.get("$aniplayUrl/api/anime/advanced-search?page=0&size=36&query=$title&startYear=$year").text) + val links = response.filter { it.websites.joinToString().contains("anime/$malId") } + .map { "https://aniplay.it/api/anime/${it.id}" } + + links.apmap { url -> + val response = parseJson(app.get(url).text) + val AnimeName = if (isDub(response.title)) { + "AniPlay DUB" + } else { + "AniPlay SUB" + } + if (response.seasons.isNullOrEmpty()) { + val episodeUrl = + "$aniplayUrl/api/episode/${response.episodes.find { it.number.toInt() == episode }?.id}" + val streamUrl = + parseJson(app.get(episodeUrl).text).url + callback.invoke( + ExtractorLink( + name, + AnimeName, + streamUrl, + referer = mainUrl, + quality = Qualities.Unknown.value, + isM3u8 = streamUrl.contains(".m3u8"), + ) + ) + } else { + val seasonid = response.seasons.sortedBy { it.episodeStart } + .last { it.episodeStart < episode!! } + val episodesData = + tryParseJson>( + app.get( + "$url/season/${seasonid.id}" + ).text + ) + val episodeData = episodesData?.find { it.number == episode.toString() }?.id + if (episodeData != null) { + val streamUrl = + parseJson(app.get("$aniplayUrl/api/episode/${episodeData}").text).url + callback.invoke( + ExtractorLink( + name, + AnimeName, + streamUrl, + referer = mainUrl, + quality = Qualities.Unknown.value, + isM3u8 = streamUrl.contains(".m3u8"), + ) + ) + } + } + } + + } + + suspend fun invoAnimeSaturn( + malId: String?, + title: String?, + episode: Int?, + callback: (ExtractorLink) -> Unit + ) { + val document = + app.get("$animesaturnUrl/animelist?search=${title?.replace("-", " ")}").document + val links = document.select("div.item-archivio").map { + it.select("a.badge-archivio").first()!!.attr("href") + } + links.apmap { url -> + val response = app.get(url).document + val AnimeName = if (isDub(response.select("img.cover-anime").first()!!.attr("alt"))) { + "AnimeSaturn DUB" + } else { + "AnimeSaturn SUB" + } + var malID: String? + + response.select("a[href*=myanimelist]").forEach { + malID = it.attr("href").substringBeforeLast("/") + .substringAfterLast("/") //https://myanimelist.net/anime/19/ -> 19 + if (malId == malID) { + val link = response.select("a.bottone-ep") + .find { it.text().substringAfter("Episodio ") == episode.toString() } + ?.attr("href") + if (link?.isBlank() == false) { //links exists + val page = app.get(link).document + val episodeLink = page.select("div.card-body > a[href*=watch]").attr("href") + ?: throw ErrorLoadingException("No link Found") + + val episodePage = app.get(episodeLink).document + val episodeUrl: String? = episodePage.select("video.afterglow > source") + .let { // Old player + it.first()?.attr("src") + } + ?: //new player + Regex("\"(https[A-z0-9\\/\\:\\.]*\\.m3u8)\",").find( + episodePage.select("script").find { + it.toString().contains("jwplayer('player_hls').setup({") + }.toString() + )?.value + + callback.invoke( + ExtractorLink( + name, + AnimeName, + episodeUrl!!, + isM3u8 = episodeUrl.contains(".m3u8"), + referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too + quality = Qualities.Unknown.value + ) + ) + } + + } + } + + } + } +} + + +private fun isDub(title: String?): Boolean { + return title?.contains(" (ITA)") ?: false +} + +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" + } + +} diff --git a/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt b/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt new file mode 100644 index 0000000..01c3a4f --- /dev/null +++ b/SoraItalianStream/src/main/kotlin/SoraItalianStream.kt @@ -0,0 +1,506 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.SoraItalianExtractor.invoGuardare +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer +import com.lagradost.cloudstream3.metaproviders.TmdbProvider +import com.lagradost.SoraItalianExtractor.invoAniPlay +import com.lagradost.SoraItalianExtractor.invoAnimeSaturn +import com.lagradost.SoraItalianExtractor.invoAnimeWorld +import com.lagradost.SoraItalianExtractor.invoCb01 +import com.lagradost.SoraItalianExtractor.invoFilmpertutti +import com.lagradost.SoraItalianExtractor.invoGuardaserie +import com.lagradost.cloudstream3.utils.AppUtils.parseJson +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.ExtractorLink +import kotlin.math.roundToInt + +open class SoraItalianStream : TmdbProvider() { + override var name = "SoraStreamItaliano" + override val hasMainPage = true + override val hasDownloadSupport = true + override val instantLinkLoading = true + override val useMetaLoadResponse = true + override var lang = "it" + override val hasChromecastSupport = true + override val supportedTypes = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.Anime, + ) + + /** AUTHOR : Adippe & Hexated & Sora */ + companion object { + private const val tmdbAPI = "https://api.themoviedb.org/3" + private const val apiKey = "71f37e6dff3b879fa4656f19547c418c" // PLEASE DON'T STEAL + const val guardaserieUrl = "https://guardaserie.app" + const val filmpertuttiUrl = "https://www.filmpertutti.skin" + const val cb01Url = "https://cb01.tours/" + const val animeworldUrl = "https://www.animeworld.tv" + const val aniplayUrl = "https://aniplay.it" + const val animesaturnUrl = "https://www.animesaturn.in" + const val tmdb2mal = "https://tmdb2mal.slidemovies.org" + fun getType(t: String?): TvType { + return when (t) { + "movie" -> TvType.Movie + else -> TvType.TvSeries + } + } + + fun getActorRole(t: String?): ActorRole { + return when (t) { + "Acting" -> ActorRole.Main + else -> ActorRole.Background + } + } + + + fun getStatus(t: String?): ShowStatus { + return when (t) { + "Returning Series" -> ShowStatus.Ongoing + else -> ShowStatus.Completed + } + } + + fun base64DecodeAPI(api: String): String { + return api.chunked(4).map { base64Decode(it) }.reversed().joinToString("") + } + + fun containsJapaneseCharacters(str: String?): Boolean { //sometimes the it-It translation of names gives the japanese name of the anime + val japaneseCharactersRegex = + Regex("[\\u3000-\\u303f\\u3040-\\u309f\\u30a0-\\u30ff\\uff00-\\uff9f\\u4e00-\\u9faf\\u3400-\\u4dbf]") + return str?.let { japaneseCharactersRegex.containsMatchIn(it) } == true + } + + } + + + override val mainPage = mainPageOf( + "$tmdbAPI/movie/popular?api_key=$apiKey®ion=&language=it-IT&page=" to "Film Popolari", + "$tmdbAPI/tv/popular?api_key=$apiKey®ion=&language=it-IT&page=" to "Serie TV Popolari", + "$tmdbAPI/discover/tv?api_key=$apiKey&with_keywords=210024|222243&page=" to "Anime", + "$tmdbAPI/movie/top_rated?api_key=$apiKey®ion=&language=it-IT&page=" to "Film più votati", + "$tmdbAPI/tv/top_rated?api_key=$apiKey®ion=&language=it-IT&page=" to "Serie TV più votate", + "$tmdbAPI/discover/tv?api_key=$apiKey&language=it-IT&with_networks=213&page=" to "Netflix", + "$tmdbAPI/discover/tv?api_key=$apiKey&language=it-IT&with_networks=1024&page=" to "Amazon", + "$tmdbAPI/discover/tv?api_key=$apiKey&language=it-IT&with_networks=2739&page=" to "Disney+", + "$tmdbAPI/discover/tv?api_key=$apiKey&language=it-IT&with_networks=453&page=" to "Hulu", + "$tmdbAPI/discover/tv?api_key=$apiKey&language=it-IT&with_networks=2552&page=" to "Apple TV+" + ) + + private fun getImageUrl(link: String?): String? { + if (link == null) return null + return if (link.startsWith("/")) "https://image.tmdb.org/t/p/w500/$link" else link + } + + private fun getOriImageUrl(link: String?): String? { + if (link == null) return null + return if (link.startsWith("/")) "https://image.tmdb.org/t/p/original/$link" else link + } + + override suspend fun getMainPage( + page: Int, + request: MainPageRequest + ): HomePageResponse { + val type = if (request.data.contains("/movie")) "movie" else "tv" + val home = app.get(request.data + page) + .parsedSafe()?.results + ?.mapNotNull { media -> + media.toSearchResponse(type) + } ?: throw ErrorLoadingException("Invalid Json reponse") + return newHomePageResponse(request.name, home) + } + + private fun Media.toSearchResponse(type: String? = null): SearchResponse? { + return newMovieSearchResponse( + title ?: name ?: originalTitle ?: return null, + Data(id = id, type = mediaType ?: type).toJson(), + TvType.Movie, + ) { + this.posterUrl = getImageUrl(posterPath) + } + } + + + override suspend fun search(query: String): List? { + return app.get( + "$tmdbAPI/search/multi?api_key=$apiKey&language=it-IT&query=$query&page=1&include_adult=false" + ) + .parsedSafe()?.results?.mapNotNull { media -> + media.toSearchResponse() + } + } + + + override suspend fun load(url: String): LoadResponse? { + val data = parseJson(url) + + val type = getType(data.type) + + val typename = data.type + + var res = + app.get("$tmdbAPI/$typename/${data.id}?api_key=$apiKey&language=it-IT&append_to_response=external_ids,credits,recommendations,videos") + .parsedSafe() ?: throw ErrorLoadingException("Invalid Json Response") + if (containsJapaneseCharacters(res.name)) { + res = + app.get("$tmdbAPI/$typename/${data.id}?api_key=$apiKey&language=en-US&append_to_response=external_ids,credits,recommendations,videos") + .parsedSafe() ?: throw ErrorLoadingException("Invalid Json Response") + } + + val title = res.name ?: res.title ?: return null + val orgTitle = res.originalName ?: res.originalTitle ?: return null + + val year = (res.tvDate ?: res.movieDate)?.split("-")?.first()?.toIntOrNull() + + val actors = res.credits?.cast?.mapNotNull { cast -> + ActorData( + Actor( + cast.name ?: cast.originalName ?: return@mapNotNull null, + getImageUrl(cast.profilePath) + ), + getActorRole(cast.knownForDepartment) + ) + } ?: return null + val recommendations = + res.recommandations?.results?.mapNotNull { media -> media.toSearchResponse() } + + val trailer = + res.videos?.results?.map { "https://www.youtube.com/watch?v=${it.key}" } + ?.randomOrNull() + + val isAnime = res.genres?.any { it.id == 16L } == true + + return if (type == TvType.Movie) { //can be a movie or a anime movie + newMovieLoadResponse( + title, + url, + TvType.Movie, + LinkData( + data.id, + res.externalIds?.imdbId, + data.type, + title = title, + year = year, + orgTitle = orgTitle, + isAnime = isAnime + ).toJson(), + ) { + this.posterUrl = getOriImageUrl(res.backdropPath) + this.year = year + this.plot = res.overview + this.tags = res.genres?.mapNotNull { it.name } + this.recommendations = recommendations + this.actors = actors + addTrailer(trailer) + } + } else { //can be anime series or tv series + val episodes = mutableListOf() + var seasonNum = 0 + val seasonDataList = res.seasons?.filter { it.seasonNumber != 0 }?.apmap { season -> + app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT") + .parsedSafe()?.episodes + } + + res.seasons?.filter { it.seasonNumber != 0 }?.forEachIndexed { index, season -> + val seasonData = seasonDataList?.get(index) + if (seasonData?.first()?.episodeNumber == 1) + seasonNum += 1 + + seasonData?.forEach { eps -> + episodes.add(Episode( + LinkData( + data.id, + res.externalIds?.imdbId, + data.type, + seasonNum, + eps.episodeNumber, + title = title, + year = year ?: season.airDate?.split("-")?.first()?.toIntOrNull(), + orgTitle = orgTitle, + isAnime = isAnime + ).toJson(), + name = eps.name, + season = seasonNum, + episode = eps.episodeNumber, + posterUrl = getImageUrl(eps.stillPath), + rating = eps.voteAverage?.times(10)?.roundToInt(), + description = eps.overview + ).apply { + this.addDate(eps.airDate) + }) + } + } + newTvSeriesLoadResponse( + title, + url, + TvType.TvSeries, + episodes + ) { + this.posterUrl = getOriImageUrl(res.backdropPath) + this.year = year + this.plot = res.overview + this.tags = res.genres?.mapNotNull { it.name } + this.showStatus = getStatus(res.status) + this.recommendations = recommendations + this.actors = actors + addTrailer(trailer) + } + } + } + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + + val res = parseJson(data) + val malID = app.get("$tmdb2mal/?id=${res.id}&s=${res.season}").text.trim() + argamap( + { + if (res.isAnime) invoAnimeWorld( + malID, + res.title, + res.episode, + callback + ) + }, + { + if (res.isAnime) invoAniPlay( + malID, + res.title, + res.episode, + res.year, + callback + ) + }, + { + if (res.isAnime) invoAnimeSaturn( + malID, + res.title, + res.episode, + callback + ) + }, + { + if (res.type == "movie") invoGuardare( + res.imdbId, + subtitleCallback, + callback + ) //just movies/anime movie + }, + { + if (res.type == "tv") invoGuardaserie( //has tv series and anime + res.imdbId, + res.season, + res.episode, + subtitleCallback, + callback + ) + }, + { + invoFilmpertutti( //has tv series, film and some anime + res.imdbId, + res.title, + res.type, + res.season, + res.episode, + res.year, + subtitleCallback, + callback + ) + }, + { + if (res.type == "movie") invoCb01(res.title, res.year, subtitleCallback, callback) + } + ) + + return true + } + + private data class LinkData( + val id: Int? = null, + val imdbId: String? = null, + val type: String? = null, //movie or tv + val season: Int? = null, + val episode: Int? = null, + val aniId: String? = null, + val animeId: String? = null, + val title: String? = null, + val year: Int? = null, + val orgTitle: String? = null, + val isAnime: Boolean + ) + + data class Data( + val id: Int? = null, + val type: String? = null, + val aniId: String? = null, + val malId: Int? = null, + ) + + data class Subtitles( + @JsonProperty("url") val url: String? = null, + @JsonProperty("lang") val lang: String? = null, + @JsonProperty("language") val language: String? = null, + ) + + data class Sources( + @JsonProperty("url") val url: String? = null, + @JsonProperty("quality") val quality: String? = null, + @JsonProperty("isM3U8") val isM3U8: Boolean = true, + ) + + data class LoadLinks( + @JsonProperty("sources") val sources: ArrayList? = arrayListOf(), + @JsonProperty("subtitles") val subtitles: ArrayList? = arrayListOf(), + ) + + data class Results( + @JsonProperty("results") val results: ArrayList? = arrayListOf(), + ) + + data class Media( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("original_title") val originalTitle: String? = null, + @JsonProperty("media_type") val mediaType: String? = null, + @JsonProperty("poster_path") val posterPath: String? = null, + ) + + data class Seasons( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("season_number") val seasonNumber: Int? = null, + @JsonProperty("air_date") val airDate: String? = null, + ) + + data class Cast( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("original_name") val originalName: String? = null, + @JsonProperty("character") val character: String? = null, + @JsonProperty("known_for_department") val knownForDepartment: String? = null, + @JsonProperty("profile_path") val profilePath: String? = null, + ) + + data class Episodes( + @JsonProperty("id") val id: Int? = null, + @JsonProperty("name") val name: String? = null, + @JsonProperty("overview") val overview: String? = null, + @JsonProperty("air_date") val airDate: String? = null, + @JsonProperty("still_path") val stillPath: String? = null, + @JsonProperty("vote_average") val voteAverage: Double? = null, + @JsonProperty("episode_number") val episodeNumber: Int? = null, + @JsonProperty("season_number") val seasonNumber: Int? = null, + ) + + data class MediaDetailEpisodes( + @JsonProperty("episodes") val episodes: ArrayList? = arrayListOf(), + ) + + + data class MovieDetails( + val adult: Boolean? = null, + @JsonProperty("first_air_date") val tvDate: String? = null, + @JsonProperty("release_date") val movieDate: String? = null, + @JsonProperty("backdrop_path") val backdropPath: String? = null, + val genres: List? = null, + val id: Long? = null, + val name: String? = null, + val title: String? = null, + @JsonProperty("number_of_seasons") val numberOfSeasons: Long? = null, + @JsonProperty("original_name") val originalName: String? = null, + @JsonProperty("original_title") val originalTitle: String? = null, + val overview: String? = null, + val popularity: Double? = null, + @JsonProperty("poster_path") val posterPath: String? = null, + val seasons: List? = null, + val status: String? = null, + val tagline: String? = null, + val type: String? = null, + @JsonProperty("vote_average") val voteAverage: Double? = null, + @JsonProperty("vote_count") val voteCount: Long? = null, + @JsonProperty("credits") val credits: Credits? = null, + @JsonProperty("recommendations") val recommandations: Recommendations? = null, + @JsonProperty("videos") val videos: Videos? = null, + @JsonProperty("external_ids") val externalIds: ExternalIds? = null + ) + + data class Recommendations( + @JsonProperty("results") val results: List? = null, + ) + + data class Credits( + val cast: List? = null, + ) + + data class ExternalIds( + @JsonProperty("imdb_id") val imdbId: String? = null + ) + + data class Videos( + val results: List? = null, + ) + + data class Trailers( + @JsonProperty("key") val key: String? = null, + ) + + data class Genre( + val id: Long? = null, + val name: String? = null + ) + + data class Season( + @JsonProperty("air_date") val airDate: String? = null, + @JsonProperty("episode_count") val episodeCount: Int? = null, + val id: Long? = null, + val name: String? = null, + val overview: String? = null, + @JsonProperty("poster_path") val posterPath: String? = null, + @JsonProperty("season_number") val seasonNumber: Int? = null + ) + + data class AnimeWorldJson( + @JsonProperty("grabber") val grabber: String, + @JsonProperty("name") val name: String, + @JsonProperty("target") val target: String, + ) + + data class AniPlayApiSearchResult( + @JsonProperty("id") val id: Int, + @JsonProperty("listWebsites") val websites: List + ) + + data class AniPlayWebsites( + @JsonProperty("url") val url: String? = null, + @JsonProperty("listWebsiteId") val websitesId: Int? = null + ) + + data class AniplayApiAnime( + @JsonProperty("episodes") val episodes: List, + @JsonProperty("seasons") val seasons: List?, + @JsonProperty("title") val title: String? + ) + + data class AniplayApiEpisode( + @JsonProperty("id") val id: Int, + @JsonProperty("title") val title: String?, + @JsonProperty("episodeNumber") val number: String, + ) + + data class AniplayApiSeason( + @JsonProperty("id") val id: Int, + @JsonProperty("name") val name: String, + @JsonProperty("episodeStart") val episodeStart: Int + ) + + data class AniPlayApiEpisodeUrl( + @JsonProperty("videoUrl") val url: String + ) +} diff --git a/SoraItalianStream/src/main/kotlin/SoraItalianStreamPlugin.kt b/SoraItalianStream/src/main/kotlin/SoraItalianStreamPlugin.kt new file mode 100644 index 0000000..5003f3b --- /dev/null +++ b/SoraItalianStream/src/main/kotlin/SoraItalianStreamPlugin.kt @@ -0,0 +1,13 @@ +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class SoraStreamItalianPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(SoraItalianStream()) + } +} \ No newline at end of file diff --git a/StarLiveProvider/build.gradle.kts b/StarLiveProvider/build.gradle.kts new file mode 100644 index 0000000..566c604 --- /dev/null +++ b/StarLiveProvider/build.gradle.kts @@ -0,0 +1,25 @@ +// use an integer for version numbers +version = 3 + + +cloudstream { + language = "it" + // All of these properties are optional, you can safely remove them + + // description = "Lorem Ipsum" + authors = listOf("Adippe") + + /** + * Status int as the following: + * 0: Down + * 1: Ok + * 2: Slow + * 3: Beta only + * */ + status = 1 // will be 3 if unspecified + tvTypes = listOf( + "Live", + ) + + iconUrl = "https://www.google.com/s2/favicons?domain=starlive.xyz&sz=%size%" +} diff --git a/StarLiveProvider/src/main/AndroidManifest.xml b/StarLiveProvider/src/main/AndroidManifest.xml new file mode 100644 index 0000000..29aec9d --- /dev/null +++ b/StarLiveProvider/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ 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 new file mode 100644 index 0000000..374e4e6 --- /dev/null +++ b/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProvider.kt @@ -0,0 +1,150 @@ +package com.lagradost + +import com.fasterxml.jackson.annotation.JsonProperty +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.network.CloudflareKiller +import com.lagradost.cloudstream3.utils.* +import com.lagradost.cloudstream3.utils.AppUtils.toJson +import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson +import org.jsoup.nodes.Element + +class StarLiveProvider : MainAPI() { + override var mainUrl = "https://starlive.xyz" + override var name = "StarLive" + override val hasMainPage = true + override var lang = "it" + override val hasChromecastSupport = true + override val supportedTypes = setOf(TvType.Live) + private val interceptor = CloudflareKiller() + + override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse { + val document = app.get(mainUrl, interceptor = interceptor).document + val sections = document.select("div.panel").groupBy { it.selectFirst("h4 b")?.text() }.values + if (sections.isEmpty()) throw ErrorLoadingException() + val prova = sections.map {elements -> + val home = elements.mapNotNull { it.toMainPageResult() } + HomePageList(elements.first()?.selectFirst("h4 b")?.text()?:"Altro", home) + } + return HomePageResponse(prova) + } + + private fun Element.toMainPageResult() : LiveSearchResponse { + val name = this.selectFirst("b")?.text()?:"Altro" + val links = this.select("tr") + .toList() + .filter { it.hasAttr("class") && it.attr("class") !in listOf("", "audio") } + .map { LinkParser( + fixUrl(it.selectFirst("a")?.attr("href")?:""), + it.attr("class"), + it.selectFirst("b")?.text()?:"" + ) } + val dayMatch = this.previousElementSiblings().toList().firstOrNull() { it.`is`("h3") }?.text() + + val matchData = MatchDataParser( + dayMatch?.plus(" - ".plus(this.selectFirst("#evento")?.text()?.substringBefore(" ")))?:"no data", + fixUrl( + this.selectFirst("h4")?.attr("style") + ?.substringAfter("(")?.substringBefore(")") ?: "" + ), + this.selectFirst("#evento")?.text()?.substringAfter(" ")?:"Match in $name || $dayMatch" + ) + val href = MatchParser(links, matchData) + return LiveSearchResponse( + this.selectFirst("#evento")?.text()?.substringAfter(" ")?:"Match in $name", + href.toJson(), + this@StarLiveProvider.name, + TvType.Live, + fixUrl( + this.selectFirst("h4")?.attr("style") + ?.substringAfter("(")?.substringBefore(")") ?: "" + ), + posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() + ) + } + + override suspend fun load(url: String): LoadResponse { + val matchdata = tryParseJson(url) + val poster = matchdata?.MatchData?.poster + val matchstart = matchdata?.MatchData?.time + return LiveStreamLoadResponse( + dataUrl = url, + url = matchdata?.linkData?.firstOrNull()?.link ?: mainUrl, + name = matchdata?.MatchData?.name ?: "No name", + posterUrl = poster, + plot = matchstart, + apiName = this@StarLiveProvider.name, + posterHeaders = interceptor.getCookieHeaders(mainUrl).toMap() + ) + } + + private suspend fun extractVideoLinks( + data: LinkParser, + callback: (ExtractorLink) -> Unit + ) { + val linktoStream = fixUrl(app.get(data.link, interceptor = interceptor).document.selectFirst("iframe")!!.attr("src")) + val referrerLink = if (linktoStream.contains("starlive")) { + app.get(linktoStream, referer = data.link, interceptor = interceptor).document.selectFirst("iframe")?.attr("src") + ?: linktoStream + } else { + linktoStream + } + val packed = when (linktoStream.contains("starlive")) { + true -> app.get( + referrerLink, + referer = linktoStream + ).document.select("script").toString() + false -> app.get(linktoStream, referer = data.link).document.select("script") + .select("script").toString() + } + var streamUrl = getAndUnpack(packed).substringAfter("var src=\"").substringBefore("\"") + if (streamUrl.contains("allowedDomains")){streamUrl = packed.substringAfter("source:'").substringBefore("'")} + if (!streamUrl.contains("m3u8")){ + val script = app.get(linktoStream, referer = data.link, interceptor = interceptor).document.selectFirst("body")?.selectFirst("script").toString() + streamUrl = Regex("source: [\\\"'](.*?)[\\\"']").find(script)?.groupValues?.last()?:"" + } + + callback( + ExtractorLink( + source = this.name, + name = data.name + " - " + data.language, + url = fixUrl(streamUrl), + quality = Qualities.Unknown.value, + referer = referrerLink, + isM3u8 = true + ) + ) + + } + + + override suspend fun loadLinks( + data: String, + isCasting: Boolean, + subtitleCallback: (SubtitleFile) -> Unit, + callback: (ExtractorLink) -> Unit + ): Boolean { + tryParseJson(data)?.linkData?.apmap { link -> + extractVideoLinks(link, callback) + } + + return true + } + + private data class LinkParser( + @JsonProperty("link") val link: String, + @JsonProperty("lang") val language: String, + @JsonProperty("name") val name: String + ) + + private data class MatchDataParser( + @JsonProperty("time") val time: String, + @JsonProperty("poster") val poster: String, + @JsonProperty("name") val name: String + ) + + private data class MatchParser( + @JsonProperty("linkData") val linkData: List, + @JsonProperty("matchData") val MatchData: MatchDataParser + ) + +} diff --git a/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProviderPlugin.kt b/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProviderPlugin.kt new file mode 100644 index 0000000..60d6bee --- /dev/null +++ b/StarLiveProvider/src/main/kotlin/com/lagradost/StarLiveProviderPlugin.kt @@ -0,0 +1,14 @@ + +package com.lagradost + +import com.lagradost.cloudstream3.plugins.CloudstreamPlugin +import com.lagradost.cloudstream3.plugins.Plugin +import android.content.Context + +@CloudstreamPlugin +class StarLiveProviderPlugin: Plugin() { + override fun load(context: Context) { + // All providers should be added in this manner. Please don't edit the providers list directly. + registerMainAPI(StarLiveProvider()) + } +} \ No newline at end of file diff --git a/StreamingcommunityProvider/build.gradle.kts b/StreamingcommunityProvider/build.gradle.kts index eb0eef0..d8dfaef 100644 --- a/StreamingcommunityProvider/build.gradle.kts +++ b/StreamingcommunityProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 2 +version = 6 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: @@ -22,5 +22,5 @@ cloudstream { "Movie", ) - iconUrl = "https://www.google.com/s2/favicons?domain=streamingcommunity.best&sz=%size%" -} \ No newline at end of file + 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 b3cc074..7bba768 100644 --- a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt +++ b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt @@ -2,14 +2,247 @@ package com.lagradost import android.text.Html import com.fasterxml.jackson.annotation.* -import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.AppUtils.parseJson 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.firstOrNull() + val posterUrl = if (img != null){ + val number = translateNumber(this.images[0].serverID.toInt()) + val ip = translateIp(this.images[0].proxyID.toInt()) + "https://$ip/images/$number/${img.url}" + } else { + "" + } + 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 +288,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,315 +355,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.tech" - 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).map { 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.map { 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 = 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).map { 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 uno = "$expire$ip Yc8U6r8KjAKAepEA".toByteArray() - val due = MessageDigest.getInstance("MD5").digest(uno) - val tre = base64Encode(due) - val token = tre.replace("=", "").replace("+", "-").replace("/", "_") - - - val link = "https://scws.xyz/master/$scwsid?token=$token&expires=$expire&n=1&n=1" - getM3u8Qualities(link, data, URI(link).host).forEach(callback) - return true - } -} diff --git a/TantiFilmProvider/build.gradle.kts b/TantiFilmProvider/build.gradle.kts index f98a6f1..bae85da 100644 --- a/TantiFilmProvider/build.gradle.kts +++ b/TantiFilmProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 5 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: @@ -23,5 +23,5 @@ cloudstream { ) - iconUrl = "https://www.google.com/s2/favicons?domain=tantifilm.autos&sz=%size%" + iconUrl = "https://www.google.com/s2/favicons?domain=tantifilm.mobi&sz=%size%" } \ No newline at end of file diff --git a/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt b/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt index ed1c6e6..63352a2 100644 --- a/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt +++ b/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt @@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.network.CloudflareKiller class TantifilmProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://tantifilm.autos" + override var mainUrl = "https://tantifilm.recipes" override var name = "Tantifilm" override val hasMainPage = true override val hasChromecastSupport = true @@ -52,7 +52,7 @@ class TantifilmProvider : MainAPI() { override suspend fun search(query: String): List { val queryformatted = query.replace(" ", "+") - val url = "$mainUrl/search/$queryformatted" + val url = "$mainUrl/?s=$queryformatted" val doc = app.get(url, interceptor = interceptor).document return doc.select("div.film.film-2").map { @@ -242,4 +242,4 @@ class TantifilmProvider : MainAPI() { } return true } -} \ No newline at end of file +} diff --git a/TvItalianaProvider/build.gradle.kts b/TvItalianaProvider/build.gradle.kts index 745691c..73f173c 100644 --- a/TvItalianaProvider/build.gradle.kts +++ b/TvItalianaProvider/build.gradle.kts @@ -1,5 +1,5 @@ // use an integer for version numbers -version = 1 +version = 2 cloudstream { @@ -7,7 +7,7 @@ cloudstream { // All of these properties are optional, you can safely remove them // description = "Lorem Ipsum" - // authors = listOf("Cloudburst") + authors = listOf("Adippe") /** * Status int as the following: diff --git a/TvItalianaProvider/src/main/kotlin/com/lagradost/TvItalianaProvider.kt b/TvItalianaProvider/src/main/kotlin/com/lagradost/TvItalianaProvider.kt index d0436c7..0e5305c 100644 --- a/TvItalianaProvider/src/main/kotlin/com/lagradost/TvItalianaProvider.kt +++ b/TvItalianaProvider/src/main/kotlin/com/lagradost/TvItalianaProvider.kt @@ -1,15 +1,19 @@ package com.lagradost +import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.utils.AppUtils.parseJson import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.nicehttp.RequestBodyTypes +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody.Companion.toRequestBody import java.io.InputStream +import java.util.UUID class TvItalianaProvider : MainAPI() { override var lang = "it" - override var mainUrl = "https://raw.githubusercontent.com/Tundrak/IPTV-Italia/main/iptvitaplus.m3u" override var name = "TvItaliana" override val hasMainPage = true override val hasChromecastSupport = true @@ -21,29 +25,85 @@ class TvItalianaProvider : MainAPI() { page: Int, request : MainPageRequest ): HomePageResponse { - val data = IptvPlaylistParser().parseM3U(app.get(mainUrl).text) - return HomePageResponse(data.items.groupBy{it.attributes["group-title"]}.map { group -> - val title = group.key ?: "" - val show = group.value.map { channel -> - val streamurl = channel.url.toString() - val channelname = channel.title.toString() - val posterurl = channel.attributes["tvg-logo"].toString() - val nation = channel.attributes["group-title"].toString() + val iptvUrl = "https://raw.githubusercontent.com/Tundrak/IPTV-Italia/main/iptvitaplus.m3u" + val data = IptvPlaylistParser().parseM3U(app.get(iptvUrl).text) + val res = data.items.groupBy{it.attributes["group-title"]}.map { group -> + val title = group.key ?: "" + val show = group.value.map { channel -> + val streamurl = channel.url.toString() + val channelname = channel.title.toString() + val posterurl = channel.attributes["tvg-logo"].toString() + val nation = channel.attributes["group-title"].toString() + LiveSearchResponse( + channelname, + LoadData(streamurl, channelname, posterurl, nation, false).toJson(), + this@TvItalianaProvider.name, + TvType.Live, + posterurl, + lang = "ita" + ) + } + HomePageList( + title, + show, + isHorizontalImages = true + ) + }.toMutableList() + + val skyStreams = listOf(7,2,1).map{ n -> + app.get("https://apid.sky.it/vdp/v1/getLivestream?id=$n").parsedSafe()} + + val shows = skyStreams.map { + val posterUrl = when (it?.title){ + "MTV8" -> "https://upload.wikimedia.org/wikipedia/commons/b/ba/MTV8_logo.jpg" + else -> "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/Sky_italia_2018.png/640px-Sky_italia_2018.png" + } + LiveSearchResponse( + it?.title!!, + LoadData(it.streamingUrl!!, it.title!!, posterUrl, "", false).toJson(), + this@TvItalianaProvider.name, + TvType.Live, + posterUrl, + lang = "ita" + ) + } + res.add( + HomePageList( + "sky italia", + shows, + isHorizontalImages = true + ) + ) + + val domain = "https://" + app.get("https://prod-realmservice.mercury.dnitv.com/realm-config/www.discoveryplus.com%2Fit%2Fepg").parsedSafe()?.domain + val deviceId = UUID.randomUUID().toString().replace("-","") + val cookies = app.get("$domain/token?deviceId=$deviceId&realm=dplay&shortlived=true").cookies + val streamDatas = app.get("$domain/cms/routes/home?include=default&decorators=playbackAllowed", cookies = cookies).parsedSafe()?.included + val posterValues = streamDatas?.filter { it.type == "image" } + ?.map { it.id to it.attributes?.src } + val discoveryinfo = streamDatas?.filter { it.type == "channel" && it.attributes?.hasLiveStream == true && it.attributes.packages?.contains("Free") ?: false } + ?.map { streamInfo -> + val posterUrl = posterValues?.find { it.first == streamInfo.relationships?.images?.data?.first()?.id }?.second!! LiveSearchResponse( - channelname, - LoadData(streamurl, channelname, posterurl, nation).toJson(), + streamInfo.attributes?.name!!, + LoadData(streamInfo.id, streamInfo.attributes.name, posterUrl, streamInfo.attributes.longDescription!!, true).toJson(), this@TvItalianaProvider.name, TvType.Live, - posterurl, + posterUrl, lang = "ita" ) } + res.add( HomePageList( - title, - show, + "Discovery", + discoveryinfo!!, isHorizontalImages = true ) - }) + ) + + return HomePageResponse(res) + + } override suspend fun search(query: String): List { @@ -56,7 +116,7 @@ class TvItalianaProvider : MainAPI() { val nation = channel.attributes["group-title"].toString() LiveSearchResponse( channelname, - LoadData(streamurl, channelname, posterurl, nation).toJson(), + LoadData(streamurl, channelname, posterurl, nation,false).toJson(), this@TvItalianaProvider.name, TvType.Live, posterurl, @@ -66,20 +126,22 @@ class TvItalianaProvider : MainAPI() { override suspend fun load(url: String): LoadResponse { val data = parseJson(url) + return LiveStreamLoadResponse( data.title, data.url, this.name, url, data.poster, - plot = data.nation + plot = data.plot ) } data class LoadData( val url: String, val title: String, val poster: String, - val nation: String + val plot: String, + val discoveryBoolean: Boolean ) override suspend fun loadLinks( @@ -88,26 +150,97 @@ class TvItalianaProvider : MainAPI() { subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { + + val loadData = parseJson(data) - callback.invoke( - ExtractorLink( - this.name, - loadData.title, - loadData.url, - "", - Qualities.Unknown.value, - isM3u8 = true + + if (!loadData.discoveryBoolean) { + callback.invoke( + ExtractorLink( + this.name, + loadData.title, + loadData.url, + "", + Qualities.Unknown.value, + isM3u8 = true + ) ) - ) + } + else{ + val domain = "https://" + app.get("https://prod-realmservice.mercury.dnitv.com/realm-config/www.discoveryplus.com%2Fit%2Fepg").parsedSafe()?.domain + val deviceId = UUID.randomUUID().toString() + val cookies = app.get("$domain/token?deviceId=$deviceId&realm=dplay&shortlived=true").cookies + + val post = PostData(loadData.url, DeviceInfo(ad = false, dmr = true)).toJson() + val data = app.post("$domain/playback/v3/channelPlaybackInfo", requestBody = post.toRequestBody( + RequestBodyTypes.JSON.toMediaTypeOrNull()), cookies = cookies).text.substringAfter("\"url\" : \"").substringBefore("\"") + callback.invoke( + ExtractorLink( + this.name, + loadData.title, + data, + "", + Qualities.Unknown.value, + isM3u8 = true + ) + ) + } return true } + data class PostData( + @JsonProperty("channelId") val id: String, + @JsonProperty("deviceInfo") val deviceInfo : DeviceInfo + ) + data class DeviceInfo( + @JsonProperty("drmSupported") val dmr : Boolean, + @JsonProperty("adBlocker") val ad: Boolean, + ) + data class DomainDiscovery( + @JsonProperty("domain") val domain: String, + ) + data class DataDiscovery( + val included: List? = null + ) + + data class Included( + val attributes: IncludedAttributes? = null, + val id: String, + val relationships : IncludedRelationships? = null, + val type: String + ) + + data class IncludedRelationships( + val images: ImagesData? = null + ) + + data class ImagesData ( + val data: List? = null + ) + data class DAT ( + val id: String? = null, + ) + + + data class IncludedAttributes( + val name: String?, + val hasLiveStream : Boolean?, + val packages: List?, + val longDescription: String?, + val src: String? + ) + } data class Playlist( val items: List = emptyList(), ) +data class LivestreamResponse( + @JsonProperty("channel") val channel: String? = null, + @JsonProperty("title") val title: String? = null, + @JsonProperty("streaming_url") val streamingUrl: String? = null, +) data class PlaylistItem( val title: String? = null, val attributes: Map = emptyMap(), @@ -340,4 +473,4 @@ sealed class PlaylistParserException(message: String) : Exception(message) { class InvalidHeader : PlaylistParserException("Invalid file header. Header doesn't start with #EXTM3U") -} +} \ No newline at end of file diff --git a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt index 2c33933..93fbef8 100644 --- a/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt +++ b/WebFlix/src/main/kotlin/com/lagradost/WebFlixProviderPlugin.kt @@ -11,10 +11,10 @@ class WebFlixProviderPlugin: Plugin() { override fun load(context: Context) { // All providers should be added in this manner. Please don't edit the providers list directly. registerMainAPI(WebFlixProvider("en", "https://dhfilmtv.com", "DHFilmTv", setOf(TvType.Movie, TvType.TvSeries))) - registerMainAPI(WebFlixProvider("pl", "https://app.vodi.cc", "Vodi.cc", setOf(TvType.Movie, TvType.TvSeries))) + //registerMainAPI(WebFlixProvider("pl", "https://app.vodi.cc", "Vodi.cc", setOf(TvType.Movie, TvType.TvSeries))) // removed due to complaint from owner of site registerMainAPI(WebFlixProvider("fr", "http://www.vanflix.cm", "Vanflix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) registerMainAPI(WebFlixProvider("pt-pt", "https://www.brflix.xyz", "BrFlix", setOf(TvType.Movie, TvType.TvSeries, TvType.Live))) registerMainAPI(WebFlixProvider("ar", "https://ifilm.live", "ifilm.live", setOf(TvType.Movie, TvType.TvSeries))) registerMainAPI(WebFlixProvider("en", "https://karmadarna.com", "KarMaDarNa", setOf(TvType.NSFW))) } -} \ No newline at end of file +} diff --git a/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt b/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt index 212b5e5..323674d 100644 --- a/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt +++ b/YomoviesProvider/src/main/kotlin/com/lagradost/YomoviesProvider.kt @@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.loadExtractor import org.jsoup.nodes.Element class YomoviesProvider : MainAPI() { - override var mainUrl = "https://yomovies.homes" + override var mainUrl = "https://yomovies.fyi" override var name = "Yomovies" override val hasMainPage = true override var lang = "hi"