From a465c0e8aada63cf815d893dc4e81867f6fc8562 Mon Sep 17 00:00:00 2001
From: antonydp <38143733+antonydp@users.noreply.github.com>
Date: Tue, 22 Nov 2022 16:13:56 +0100
Subject: [PATCH] SoraItalianStream + fixes
---
AltadefinizioneProvider/build.gradle.kts | 2 +-
.../com/lagradost/AltadefinizioneProvider.kt | 2 +-
EurostreamingProvider/build.gradle.kts | 2 +-
.../com/lagradost/EurostreamingProvider.kt | 2 +-
.../com/lagradost/FilmpertuttiProvider.kt | 2 +-
SoraItalianStream/build.gradle.kts | 29 +
SoraItalianStream/src/AndroidManifest.xml | 2 +
.../src/kotlin/SoraItalianExtractor.kt | 403 ++++++++++++++
.../src/kotlin/SoraItalianStream.kt | 511 ++++++++++++++++++
.../src/kotlin/SoraItalianStreamPlugin.kt | 13 +
.../lagradost/StreamingcommunityProvider.kt | 2 +-
.../kotlin/com/lagradost/TantiFilmProvider.kt | 2 +-
12 files changed, 965 insertions(+), 7 deletions(-)
create mode 100644 SoraItalianStream/build.gradle.kts
create mode 100644 SoraItalianStream/src/AndroidManifest.xml
create mode 100644 SoraItalianStream/src/kotlin/SoraItalianExtractor.kt
create mode 100644 SoraItalianStream/src/kotlin/SoraItalianStream.kt
create mode 100644 SoraItalianStream/src/kotlin/SoraItalianStreamPlugin.kt
diff --git a/AltadefinizioneProvider/build.gradle.kts b/AltadefinizioneProvider/build.gradle.kts
index ed63616..4d19548 100644
--- a/AltadefinizioneProvider/build.gradle.kts
+++ b/AltadefinizioneProvider/build.gradle.kts
@@ -1,5 +1,5 @@
// use an integer for version numbers
-version = 3
+version = 2
cloudstream {
diff --git a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt
index ed64c13..9f03630 100644
--- a/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt
+++ b/AltadefinizioneProvider/src/main/kotlin/com/lagradost/AltadefinizioneProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.html
class AltadefinizioneProvider : MainAPI() {
override var lang = "it"
- override var mainUrl = "https://altadefinizione.camera"
+ override var mainUrl = "https://altadefinizione.audio"
override var name = "Altadefinizione"
override val hasMainPage = true
override val hasChromecastSupport = true
diff --git a/EurostreamingProvider/build.gradle.kts b/EurostreamingProvider/build.gradle.kts
index 62f8d76..9defcbe 100644
--- a/EurostreamingProvider/build.gradle.kts
+++ b/EurostreamingProvider/build.gradle.kts
@@ -1,5 +1,5 @@
// use an integer for version numbers
-version = 1
+version = 2
cloudstream {
diff --git a/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt b/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt
index ec81f4e..9d41cdd 100644
--- a/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt
+++ b/EurostreamingProvider/src/main/kotlin/com/lagradost/EurostreamingProvider.kt
@@ -9,7 +9,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.toJson
class EurostreamingProvider : MainAPI() {
override var lang = "it"
- override var mainUrl = "https://eurostreaming.social"
+ override var mainUrl = "https://eurostreaming.taxi"
override var name = "Eurostreaming"
override val hasMainPage = true
override val hasChromecastSupport = true
diff --git a/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt b/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt
index d1fd283..3cb9fc6 100644
--- a/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt
+++ b/FilmpertuttiProvider/src/main/kotlin/com/lagradost/FilmpertuttiProvider.kt
@@ -16,7 +16,7 @@ import com.lagradost.cloudstream3.network.CloudflareKiller
class FilmpertuttiProvider : MainAPI() {
override var lang = "it"
- override var mainUrl = "https://filmpertutti.hair"
+ override var mainUrl = "https://filmpertutti.skin"
override var name = "FilmPerTutti"
override val hasMainPage = true
override val hasChromecastSupport = true
diff --git a/SoraItalianStream/build.gradle.kts b/SoraItalianStream/build.gradle.kts
new file mode 100644
index 0000000..26a021a
--- /dev/null
+++ b/SoraItalianStream/build.gradle.kts
@@ -0,0 +1,29 @@
+// use an integer for version numbers
+version = 1
+
+
+cloudstream {
+ language = "es"
+ // 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")
+
+ /**
+ * 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/AndroidManifest.xml b/SoraItalianStream/src/AndroidManifest.xml
new file mode 100644
index 0000000..0474547
--- /dev/null
+++ b/SoraItalianStream/src/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/SoraItalianStream/src/kotlin/SoraItalianExtractor.kt b/SoraItalianStream/src/kotlin/SoraItalianExtractor.kt
new file mode 100644
index 0000000..6938eaf
--- /dev/null
+++ b/SoraItalianStream/src/kotlin/SoraItalianExtractor.kt
@@ -0,0 +1,403 @@
+package com.lagradost
+
+import com.lagradost.AniPlayProvider
+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.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").map { source ->
+ loadExtractor(
+ fixUrl(source.attr("data-link")),
+ "$/",
+ subtitleCallback,
+ callback
+ )
+ println("LINK DI Guardare " + fixUrl(source.attr("data-link")))
+ }
+ }
+
+ 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").mapIndexed { seasonData, data ->
+ data.select("li").mapIndexed { epNum, epData ->
+ if (season == seasonData + 1 && episode == epNum + 1) {
+ epData.select("div.mirrors > a").map {
+ loadExtractor(
+ fixUrl(it.attr("data-link")),
+ "$/",
+ subtitleCallback,
+ callback
+ )
+ println("LINK DI guardaserie " + it.attr("data-link"))
+ }
+ }
+ }
+ }
+ }
+
+ 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")?.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")
+ .filter { it.isDigit() }.toIntOrNull()
+ }
+
+ episodeData?.select("#links > div > div > table > tbody:nth-child(2) > tr")
+ ?.map {
+ loadExtractor(
+ it.selectFirst("a")!!.attr("href") ?: "",
+ filmpertuttiUrl,
+ subtitleCallback,
+ callback
+ )
+ println("FIlmpetutti " + it.selectFirst("a")!!.attr("href") ?: "")
+ }
+ } else {
+ val urls0 = doc.select("div.embed-player")
+ if (urls0.isNotEmpty()) {
+ urls0.map {
+ loadExtractor(
+ it.attr("data-id"),
+ filmpertuttiUrl,
+ subtitleCallback,
+ callback
+
+ )
+ println("LINK DI FIlmpetutti " + it.attr("data-id"))
+ }
+ } else {
+ doc.select("#info > ul > li ").mapNotNull {
+ val link = it.selectFirst("a")?.attr("href") ?: ""
+ loadExtractor(
+ ShortLink.unshorten(link).trim().replace("/v/", "/e/")
+ .replace("/f/", "/e/"),
+ "$/",
+ subtitleCallback,
+ callback
+ )
+ println("LINK DI FIlmpetutti " + it.selectFirst("a")?.attr("href"))
+ }
+ }
+ }
+ }
+ }
+ }
+
+ suspend fun invoAltadefinizione(
+ id: String? = null,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val url = app.get(
+ "$altadefinizioneUrl/index.php?story=$id&do=search&subaction=search"
+ ).document.selectFirst("div.cover_kapsul > a")?.attr("href") ?: return
+ val document = app.get(url).document
+ document.select("ul.host>a").map {
+ loadExtractor(
+ fixUrl(it.attr("data-link")),
+ altadefinizioneUrl,
+ subtitleCallback,
+ callback
+ )
+ println("LINK DI altadefinizione " + fixUrl(it.attr("data-link")))
+ }
+
+ }
+
+ suspend fun invoCb01(
+ title: String?,
+ year: Int?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ callback: (ExtractorLink) -> Unit
+ ) {
+ val res = app.get("$cb01Url/search/$title $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").mapNotNull {
+ val link = it.selectFirst("a")?.attr("href") ?: ""
+ val url = ShortLink.unshorten(link).trim().replace("/v/", "/e/")
+ .replace("/f/", "/e/")
+ val processedUrl = if (url.contains("mixdrop.club")){
+ fixUrl(app.get(url).document.selectFirst("iframe")?.attr("src")?:"")
+ }
+ else{url}
+ loadExtractor(
+ processedUrl,
+ "$/",
+ subtitleCallback,
+ callback
+ )
+ println("LINK DI CB01 " + url)
+ }
+
+ }
+ }
+ suspend fun invoAnimeWorld(
+ malId: String?,
+ title: String?,
+ episode: Int?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ 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 malID = document.select("#mal-button").attr("href")
+ .split('/').last().toString()
+ if (malId == malID) {
+ 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 }
+ .map { 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
+ )
+ )
+ println("LINK DI Animeworld " + url)
+ }
+ }
+ }
+ }
+
+ suspend fun invoAniPlay(
+ malId: String?,
+ title: String?,
+ episode: Int?,
+ year: Int?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ 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"),
+ )
+ )
+ println("LINK DI aniplay " + streamUrl)
+ }
+ }
+ }
+
+ }
+
+ suspend fun invoAnimeSaturn(
+ malId: String?,
+ title: String?,
+ episode: Int?,
+ subtitleCallback: (SubtitleFile) -> Unit,
+ 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? = null
+
+ response.select("[rel=\"noopener noreferrer\"]").forEach {
+ if(it.attr("href").contains("myanimelist"))
+ malID = it.attr("href").removeSuffix("/").split('/').last()
+ if (malId == malID){
+ val link = response.select("a.bottone-ep").find { it.text().split(" ")[1] == episode.toString() }?.attr("href")
+ if (link != null) {
+ val page = app.get(link).document
+ val episodeLink = page.select("div.card-body > a[href]").find { it1 ->
+ it1.attr("href").contains("watch?")
+ }?.attr("href") ?: throw ErrorLoadingException("No link Found")
+
+ val episodePage = app.get(episodeLink).document
+ val episodeUrl: String?
+ var isM3U8 = false
+
+ if (episodePage.select("video.afterglow > source").isNotEmpty()) // Old player
+ episodeUrl =
+ episodePage.select("video.afterglow > source").first()!!.attr("src")
+ else { // New player
+ val script = episodePage.select("script").find {
+ it.toString().contains("jwplayer('player_hls').setup({")
+ }!!.toString()
+ episodeUrl = script.split(" ")
+ .find { it.contains(".m3u8") and !it.contains(".replace") }!!
+ .replace("\"", "").replace(",", "")
+ isM3U8 = true
+ }
+
+ callback.invoke(
+ ExtractorLink(
+ name,
+ AnimeName,
+ episodeUrl!!,
+ isM3u8 = isM3U8,
+ referer = "https://www.animesaturn.io/", //Some servers need the old host as referer, and the new ones accept it too
+ quality = Qualities.Unknown.value
+ )
+ )
+ println("LINK DI animesaturn " + episodeUrl)
+ }
+
+ }
+ }
+
+ }
+ }
+}
+
+
+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/kotlin/SoraItalianStream.kt b/SoraItalianStream/src/kotlin/SoraItalianStream.kt
new file mode 100644
index 0000000..f5dd982
--- /dev/null
+++ b/SoraItalianStream/src/kotlin/SoraItalianStream.kt
@@ -0,0 +1,511 @@
+package com.lagradost
+
+import com.fasterxml.jackson.annotation.JsonProperty
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoGuardare
+import com.lagradost.cloudstream3.*
+import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
+import com.lagradost.cloudstream3.metaproviders.TmdbProvider
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoAltadefinizione
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoAniPlay
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoAnimeSaturn
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoAnimeWorld
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoCb01
+import com.lagradost.SoraItalian.SoraItalianExtractor.invoFilmpertutti
+import com.lagradost.SoraItalian.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 altadefinizioneUrl = "https://altadefinizione01.autos"
+ const val cb01Url = "https://cb01.delivery"
+ 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("")
+ }
+
+ }
+
+
+ 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 {
+ val searchResponse = mutableListOf()
+
+ val mainResponse = app.get(
+ "$tmdbAPI/search/multi?api_key=$apiKey&language=it-IT&query=$query&page=1&include_adult=false"
+ ).parsedSafe()?.results?.mapNotNull { media ->
+ media.toSearchResponse()
+ }
+ if (mainResponse?.isNotEmpty() == true) searchResponse.addAll(mainResponse)
+
+// val animeResponse =
+// app.get("$mainServerAPI/search/anime/$query?_data=routes/search/anime/\$animeKeyword")
+// .parsedSafe()?.searchResults?.results?.mapNotNull { anime -> anime.toSearchResponse() }
+// if (animeResponse?.isNotEmpty() == true) searchResponse.addAll(animeResponse)
+
+ return searchResponse
+ }
+
+
+ override suspend fun load(url: String): LoadResponse? {
+ val data = parseJson(url)
+
+ val type = getType(data.type)
+
+ val typename = when(type){
+ TvType.TvSeries-> "tv"
+ TvType.Movie -> "movie"
+ else -> ""
+ }
+
+ val 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")
+
+
+ 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()
+
+ if (res.genres?.map { it.id }?.contains(16) == true && type == TvType.TvSeries) {
+
+ val episodes = mutableListOf()
+ res.seasons?.filter { it.seasonNumber != 0L }?.apmap { season ->
+ val seasonData = app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT")
+ .parsedSafe()?.episodes
+ val seasonID = if (season.seasonNumber!! > 1 && seasonData?.isNotEmpty() == true && seasonData?.first()?.episodeNumber != 1){ 1 } else { season.seasonNumber }.toInt()
+ seasonData?.map { eps ->
+ episodes.add(Episode(
+ LinkData(
+ data.id,
+ res.externalIds?.imdbId,
+ data.type,
+ seasonID,
+ eps.episodeNumber,
+ title = title,
+ year = year,
+ orgTitle = orgTitle,
+ isAnime = true
+ ).toJson(),
+ name = eps.name,
+ season = eps.seasonNumber,
+ episode = eps.episodeNumber,
+ posterUrl = getImageUrl(eps.stillPath),
+ rating = eps.voteAverage?.times(10)?.roundToInt(),
+ description = eps.overview
+ ).apply {
+ this.addDate(eps.airDate)
+ })
+ }
+ }
+ return 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)
+ }
+
+ }
+ return if (type == TvType.TvSeries) {
+ val episodes = mutableListOf()
+ res.seasons?.filter { it.seasonNumber != 0L }?.apmap { season ->
+ app.get("$tmdbAPI/${data.type}/${data.id}/season/${season.seasonNumber}?api_key=$apiKey&language=it-IT")
+ .parsedSafe()?.episodes?.map { eps ->
+ episodes.add(Episode(
+ LinkData(
+ data.id,
+ res.externalIds?.imdbId,
+ data.type,
+ eps.seasonNumber,
+ eps.episodeNumber,
+ title = title,
+ year = season.airDate?.split("-")?.first()?.toIntOrNull(),
+ orgTitle = orgTitle,
+ isAnime = false
+ ).toJson(),
+ name = eps.name,
+ season = eps.seasonNumber,
+ 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)
+ }
+ }
+ else {
+ newMovieLoadResponse(
+ title,
+ url,
+ TvType.Movie,
+ LinkData(
+ data.id,
+ res.externalIds?.imdbId,
+ data.type,
+ title = title,
+ year = year,
+ orgTitle = orgTitle,
+ isAnime = res.genres?.map { it.id }?.contains(16)?:false
+ ).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)
+ }
+ }
+ }
+
+ 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, subtitleCallback, callback)
+ },
+ {
+ if (res.isAnime) invoAniPlay(malID, res.title, res.episode, res.year, subtitleCallback, callback)
+ },
+ {
+ if (res.isAnime) invoAnimeSaturn(malID, res.title, res.episode, subtitleCallback, callback)
+ },
+ {
+ invoGuardare(res.imdbId, subtitleCallback, callback)
+ },
+ {
+ if (malID == "") invoGuardaserie(res.imdbId, res.season, res.episode, subtitleCallback, callback)
+ },
+ {
+ if (!res.isAnime) invoFilmpertutti(res.imdbId,res.title, res.type, res.season, res.episode, res.year, subtitleCallback, callback)
+ },
+ {
+ if (!res.isAnime) invoAltadefinizione(res.imdbId, subtitleCallback, callback)
+ },
+ {
+ if (!res.isAnime) 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,
+ 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: Long? = 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: Long? = 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
+ )
+}
\ No newline at end of file
diff --git a/SoraItalianStream/src/kotlin/SoraItalianStreamPlugin.kt b/SoraItalianStream/src/kotlin/SoraItalianStreamPlugin.kt
new file mode 100644
index 0000000..59ca414
--- /dev/null
+++ b/SoraItalianStream/src/kotlin/SoraItalianStreamPlugin.kt
@@ -0,0 +1,13 @@
+package com.hexated
+
+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/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt
index 9c38e19..0fda93b 100644
--- a/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt
+++ b/StreamingcommunityProvider/src/main/kotlin/com/lagradost/StreamingcommunityProvider.kt
@@ -128,7 +128,7 @@ data class TrailerElement(
class StreamingcommunityProvider: MainAPI() {
override var lang = "it"
- override var mainUrl = "https://streamingcommunity.help"
+ override var mainUrl = "https://streamingcommunity.cheap"
override var name = "StreamingCommunity"
override val hasMainPage = true
override val hasChromecastSupport = true
diff --git a/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt b/TantiFilmProvider/src/main/kotlin/com/lagradost/TantiFilmProvider.kt
index 500b1c8..7b8fbf9 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.tattoo"
+ override var mainUrl = "https://tantifilm.delivery"
override var name = "Tantifilm"
override val hasMainPage = true
override val hasChromecastSupport = true