mirror of
https://github.com/recloudstream/cloudstream-extensions-multilingual.git
synced 2024-08-15 03:15:14 +00:00
Fixes and new italian Providers (#1)
This commit is contained in:
parent
8e0cde9e7b
commit
d17452609f
17 changed files with 771 additions and 12 deletions
|
@ -1,6 +1,5 @@
|
||||||
package com.lagradost
|
package com.lagradost
|
||||||
|
|
||||||
//import androidx.core.text.parseAsHtml
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
|
|
@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addAniListId
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addDuration
|
||||||
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.M3u8Helper
|
import com.lagradost.cloudstream3.utils.M3u8Helper
|
||||||
import com.lagradost.cloudstream3.utils.Qualities
|
import com.lagradost.cloudstream3.utils.Qualities
|
||||||
|
@ -93,7 +94,7 @@ class AniPlayProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
private suspend fun ApiSeason.toEpisodeList(url: String) : List<Episode> {
|
private suspend fun ApiSeason.toEpisodeList(url: String) : List<Episode> {
|
||||||
return app.get("$url/season/${this.id}").parsed<List<ApiEpisode>>().mapNotNull { it.toEpisode() }
|
return parseJson<List<ApiEpisode>>(app.get("$url/season/${this.id}").text).mapNotNull { it.toEpisode() }
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ApiAnime(
|
data class ApiAnime(
|
||||||
|
@ -115,7 +116,7 @@ class AniPlayProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
|
override suspend fun getMainPage(page: Int, request : MainPageRequest): HomePageResponse {
|
||||||
val response = app.get("$mainUrl/api/home/latest-episodes?page=0").parsed<List<ApiMainPageAnime>>()
|
val response = parseJson<List<ApiMainPageAnime>>(app.get("$mainUrl/api/home/latest-episodes?page=0").text)
|
||||||
|
|
||||||
val results = response.map{
|
val results = response.map{
|
||||||
val isDub = isDub(it.title)
|
val isDub = isDub(it.title)
|
||||||
|
@ -133,7 +134,7 @@ class AniPlayProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun search(query: String): List<SearchResponse> {
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
val response = app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").parsed<List<ApiSearchResult>>()
|
val response = parseJson<List<ApiSearchResult>>(app.get("$mainUrl/api/anime/advanced-search?page=0&size=36&query=$query").text)
|
||||||
|
|
||||||
return response.map {
|
return response.map {
|
||||||
val isDub = isDub(it.title)
|
val isDub = isDub(it.title)
|
||||||
|
@ -151,7 +152,7 @@ class AniPlayProvider : MainAPI() {
|
||||||
|
|
||||||
override suspend fun load(url: String): LoadResponse {
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
|
||||||
val response = app.get(url).parsed<ApiAnime>()
|
val response = parseJson<ApiAnime>(app.get(url).text)
|
||||||
|
|
||||||
val tags: List<String> = response.genres.map { it.name }
|
val tags: List<String> = response.genres.map { it.name }
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ class AniPlayProvider : MainAPI() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
||||||
val episode = app.get(data).parsed<ApiEpisodeUrl>()
|
val episode = parseJson<ApiEpisodeUrl>(app.get(data).text)
|
||||||
|
|
||||||
if(episode.url.contains(".m3u8")){
|
if(episode.url.contains(".m3u8")){
|
||||||
val m3u8Helper = M3u8Helper()
|
val m3u8Helper = M3u8Helper()
|
||||||
|
|
24
CalcioStreamingProvider/build.gradle.kts
Normal file
24
CalcioStreamingProvider/build.gradle.kts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
|
||||||
|
cloudstream {
|
||||||
|
// 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 // will be 3 if unspecified
|
||||||
|
tvTypes = listOf(
|
||||||
|
"Live",
|
||||||
|
)
|
||||||
|
|
||||||
|
iconUrl = "https://www.google.com/s2/favicons?domain=calciostreaming.live&sz=24"
|
||||||
|
}
|
2
CalcioStreamingProvider/src/main/AndroidManifest.xml
Normal file
2
CalcioStreamingProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.lagradost"/>
|
|
@ -0,0 +1,107 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.*
|
||||||
|
|
||||||
|
|
||||||
|
class CalcioStreamingProvider : MainAPI() {
|
||||||
|
override var lang = "it"
|
||||||
|
override var mainUrl = "https://calciostreaming.live"
|
||||||
|
override var name = "CalcioStreaming"
|
||||||
|
override val hasMainPage = true
|
||||||
|
override val hasChromecastSupport = true
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.Live,
|
||||||
|
|
||||||
|
)
|
||||||
|
override suspend fun getMainPage(page: Int, request: MainPageRequest): HomePageResponse {
|
||||||
|
val document = app.get(mainUrl+"/partite-streaming.html").document
|
||||||
|
val sections = document.select("div.slider-title").filter {it -> it.select("div.item").isNotEmpty()}
|
||||||
|
|
||||||
|
if (sections.isEmpty()) throw ErrorLoadingException()
|
||||||
|
|
||||||
|
return HomePageResponse(sections.map { it ->
|
||||||
|
val categoryname = it.selectFirst("h2 > strong")!!.text()
|
||||||
|
val shows = it.select("div.item").map {
|
||||||
|
val href = it.selectFirst("a")!!.attr("href")
|
||||||
|
val name = it.selectFirst("a > div > h1")!!.text()
|
||||||
|
val posterurl = fixUrl(it.selectFirst("a > img")!!.attr("src"))
|
||||||
|
LiveSearchResponse(
|
||||||
|
name,
|
||||||
|
href,
|
||||||
|
this@CalcioStreamingProvider.name,
|
||||||
|
TvType.Live,
|
||||||
|
posterurl,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HomePageList(
|
||||||
|
categoryname,
|
||||||
|
shows,
|
||||||
|
isHorizontalImages = true
|
||||||
|
)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
|
||||||
|
val document = app.get(url).document
|
||||||
|
val poster = fixUrl(document.select("#title-single > div").attr("style").substringAfter("url(").substringBeforeLast(")"))
|
||||||
|
val Matchstart = document.select("div.info-wrap > div").textNodes().joinToString("").trim()
|
||||||
|
return LiveStreamLoadResponse(
|
||||||
|
document.selectFirst(" div.info-t > h1")!!.text(),
|
||||||
|
url,
|
||||||
|
this.name,
|
||||||
|
url,
|
||||||
|
poster,
|
||||||
|
plot = Matchstart
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private suspend fun extractVideoLinks(
|
||||||
|
url: String,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
extractVideoLinks(data, callback)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 CalcioStreamingProviderPlugin: Plugin() {
|
||||||
|
override fun load(context: Context) {
|
||||||
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
|
registerMainAPI(CalcioStreamingProvider())
|
||||||
|
}
|
||||||
|
}
|
26
CineBlogProvider/build.gradle.kts
Normal file
26
CineBlogProvider/build.gradle.kts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
|
||||||
|
cloudstream {
|
||||||
|
// 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 // will be 3 if unspecified
|
||||||
|
tvTypes = listOf(
|
||||||
|
"TvSeries",
|
||||||
|
"Movie",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
iconUrl = "https://www.google.com/s2/favicons?domain=cb01.rip&sz=24"
|
||||||
|
}
|
2
CineBlogProvider/src/main/AndroidManifest.xml
Normal file
2
CineBlogProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.lagradost"/>
|
|
@ -0,0 +1,187 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
|
import com.lagradost.cloudstream3.utils.loadExtractor
|
||||||
|
|
||||||
|
|
||||||
|
class CineBlogProvider : MainAPI() {
|
||||||
|
override var lang = "it"
|
||||||
|
override var mainUrl = "https://cb01.rip"
|
||||||
|
override var name = "CineBlog"
|
||||||
|
override val hasMainPage = true
|
||||||
|
override val hasChromecastSupport = true
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.Movie,
|
||||||
|
TvType.TvSeries,
|
||||||
|
)
|
||||||
|
override val mainPage = mainPageOf(
|
||||||
|
Pair("$mainUrl/popolari/page/number/?get=movies", "Film Popolari"),
|
||||||
|
Pair("$mainUrl/popolari/page/number/?get=tv", "Serie Tv Popolari"),
|
||||||
|
Pair("$mainUrl/i-piu-votati/page/number/?get=movies", "Film più votati"),
|
||||||
|
Pair("$mainUrl/i-piu-votati/page/number/?get=tv", "Serie Tv più votate"),
|
||||||
|
Pair("$mainUrl/anno/2022/page/number", "Ultime uscite"),
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getMainPage(
|
||||||
|
page: Int,
|
||||||
|
request : MainPageRequest
|
||||||
|
): 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return newHomePageResponse(request.name, home)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
val page = app.get(url)
|
||||||
|
val document = page.document
|
||||||
|
val type = if (url.contains("film")) TvType.Movie else TvType.TvSeries
|
||||||
|
val title = document.selectFirst("div.data > h1")!!.text().substringBefore("(")
|
||||||
|
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 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<Episode>()
|
||||||
|
document.select("#seasons > div").reversed().map { element ->
|
||||||
|
val season = element.selectFirst("div.se-q > span.se-t")!!.text().toInt()
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TvSeriesLoadResponse(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
this.name,
|
||||||
|
type,
|
||||||
|
episodeList,
|
||||||
|
fixUrlNull(poster),
|
||||||
|
year.toIntOrNull(),
|
||||||
|
description,
|
||||||
|
null,
|
||||||
|
rating,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
mutableListOf(),
|
||||||
|
recomm
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val actors: List<ActorData> =
|
||||||
|
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()
|
||||||
|
val actorImage : String? = actordata.selectFirst("div.img > a > img")?.attr("src")
|
||||||
|
val roleActor = actordata.selectFirst("div.data > div.caracter")!!.text()
|
||||||
|
ActorData(actor = Actor(actorName, image = actorImage), roleString = roleActor )
|
||||||
|
}
|
||||||
|
return newMovieLoadResponse(
|
||||||
|
title,
|
||||||
|
url,
|
||||||
|
type,
|
||||||
|
url
|
||||||
|
) {
|
||||||
|
posterUrl = fixUrlNull(poster)
|
||||||
|
this.year = year.toIntOrNull()
|
||||||
|
this.plot = description
|
||||||
|
this.rating = rating
|
||||||
|
this.recommendations = recomm
|
||||||
|
this.duration = null
|
||||||
|
this.actors = actors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
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(
|
||||||
|
"content-type" to "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
"accept" to "*/*",
|
||||||
|
"X-Requested-With" to "XMLHttpRequest",
|
||||||
|
), data = mapOf(
|
||||||
|
"action" to "doo_player_ajax",
|
||||||
|
"post" to idpost,
|
||||||
|
"nume" to "1",
|
||||||
|
"type" to type,
|
||||||
|
))
|
||||||
|
|
||||||
|
val url2= Regex("""src='((.|\\n)*?)'""").find(test.text)?.groups?.get(1)?.value.toString()
|
||||||
|
val trueUrl = app.get(url2, headers = mapOf("referer" to mainUrl)).url
|
||||||
|
loadExtractor(trueUrl, data, subtitleCallback, callback)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 CineBlogProviderPlugin: Plugin() {
|
||||||
|
override fun load(context: Context) {
|
||||||
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
|
registerMainAPI(CineBlogProvider())
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ class IlGenioDelloStreamingProvider : MainAPI() {
|
||||||
link,
|
link,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
it.selectFirst("img")!!.attr("data-src-img"),
|
it.selectFirst("img")!!.attr("src"),
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
quality = quality
|
quality = quality
|
||||||
|
@ -58,7 +58,7 @@ class IlGenioDelloStreamingProvider : MainAPI() {
|
||||||
val doc = app.get(url,referer= mainUrl ).document
|
val doc = app.get(url,referer= mainUrl ).document
|
||||||
return doc.select("div.result-item").map {
|
return doc.select("div.result-item").map {
|
||||||
val href = it.selectFirst("div.image > div > a")!!.attr("href")
|
val href = it.selectFirst("div.image > div > a")!!.attr("href")
|
||||||
val poster = it.selectFirst("div.image > div > a > img")!!.attr("data-src-img")
|
val poster = it.selectFirst("div.image > div > a > img")!!.attr("src")
|
||||||
val name = it.selectFirst("div.details > div.title > a")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
val name = it.selectFirst("div.details > div.title > a")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
||||||
MovieSearchResponse(
|
MovieSearchResponse(
|
||||||
name,
|
name,
|
||||||
|
@ -84,11 +84,11 @@ class IlGenioDelloStreamingProvider : MainAPI() {
|
||||||
year = year.dropLast(4)
|
year = year.dropLast(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
val poster = document.selectFirst("div.poster > img")!!.attr("data-src-img")
|
val poster = document.selectFirst("div.poster > img")!!.attr("src")
|
||||||
|
|
||||||
val recomm = document.select("article.w_item_b").map {
|
val recomm = document.select("article.w_item_b").map {
|
||||||
val href = it.selectFirst("a")!!.attr("href")
|
val href = it.selectFirst("a")!!.attr("href")
|
||||||
val posterUrl = it.selectFirst("img")!!.attr("data-src-img")
|
val posterUrl = it.selectFirst("img")!!.attr("src")
|
||||||
val name = it.selectFirst("div.data > h3")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
val name = it.selectFirst("div.data > h3")!!.text().substringBeforeLast("(").substringBeforeLast("[")
|
||||||
MovieSearchResponse(
|
MovieSearchResponse(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -128,7 +128,7 @@ data class TrailerElement(
|
||||||
|
|
||||||
class StreamingcommunityProvider : MainAPI() {
|
class StreamingcommunityProvider : MainAPI() {
|
||||||
override var lang = "it"
|
override var lang = "it"
|
||||||
override var mainUrl = "https://streamingcommunity.best"
|
override var mainUrl = "https://streamingcommunity.agency"
|
||||||
override var name = "Streamingcommunity"
|
override var name = "Streamingcommunity"
|
||||||
override val hasMainPage = true
|
override val hasMainPage = true
|
||||||
override val hasChromecastSupport = true
|
override val hasChromecastSupport = true
|
||||||
|
|
|
@ -16,7 +16,7 @@ cloudstream {
|
||||||
* 2: Slow
|
* 2: Slow
|
||||||
* 3: Beta only
|
* 3: Beta only
|
||||||
* */
|
* */
|
||||||
status = 1 // will be 3 if unspecified
|
status = 0 // will be 3 if unspecified
|
||||||
tvTypes = listOf(
|
tvTypes = listOf(
|
||||||
"TvSeries",
|
"TvSeries",
|
||||||
"Movie",
|
"Movie",
|
||||||
|
|
24
TvItalianaProvider/build.gradle.kts
Normal file
24
TvItalianaProvider/build.gradle.kts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// use an integer for version numbers
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
|
||||||
|
cloudstream {
|
||||||
|
// 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 // will be 3 if unspecified
|
||||||
|
tvTypes = listOf(
|
||||||
|
"Live",
|
||||||
|
)
|
||||||
|
|
||||||
|
iconUrl = "https://www.google.com/s2/favicons?domain=github.com&sz=24"
|
||||||
|
}
|
2
TvItalianaProvider/src/main/AndroidManifest.xml
Normal file
2
TvItalianaProvider/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="com.lagradost"/>
|
|
@ -0,0 +1,343 @@
|
||||||
|
package com.lagradost
|
||||||
|
|
||||||
|
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 java.io.InputStream
|
||||||
|
|
||||||
|
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
|
||||||
|
override val supportedTypes = setOf(
|
||||||
|
TvType.Live,
|
||||||
|
)
|
||||||
|
|
||||||
|
override suspend fun getMainPage(
|
||||||
|
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()
|
||||||
|
LiveSearchResponse(
|
||||||
|
channelname,
|
||||||
|
LoadData(streamurl, channelname, posterurl, nation).toJson(),
|
||||||
|
this@TvItalianaProvider.name,
|
||||||
|
TvType.Live,
|
||||||
|
posterurl,
|
||||||
|
lang = "ita"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
HomePageList(
|
||||||
|
title,
|
||||||
|
show,
|
||||||
|
isHorizontalImages = true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun search(query: String): List<SearchResponse> {
|
||||||
|
val data = IptvPlaylistParser().parseM3U(app.get(mainUrl).text)
|
||||||
|
|
||||||
|
return data.items.filter { it.attributes["tvg-id"]?.contains(query) ?: false }.map { channel ->
|
||||||
|
val streamurl = channel.url.toString()
|
||||||
|
val channelname = channel.attributes["tvg-id"].toString()
|
||||||
|
val posterurl = channel.attributes["tvg-logo"].toString()
|
||||||
|
val nation = channel.attributes["group-title"].toString()
|
||||||
|
LiveSearchResponse(
|
||||||
|
channelname,
|
||||||
|
LoadData(streamurl, channelname, posterurl, nation).toJson(),
|
||||||
|
this@TvItalianaProvider.name,
|
||||||
|
TvType.Live,
|
||||||
|
posterurl,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(url: String): LoadResponse {
|
||||||
|
val data = parseJson<LoadData>(url)
|
||||||
|
return LiveStreamLoadResponse(
|
||||||
|
data.title,
|
||||||
|
data.url,
|
||||||
|
this.name,
|
||||||
|
url,
|
||||||
|
data.poster,
|
||||||
|
plot = data.nation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
data class LoadData(
|
||||||
|
val url: String,
|
||||||
|
val title: String,
|
||||||
|
val poster: String,
|
||||||
|
val nation: String
|
||||||
|
|
||||||
|
)
|
||||||
|
override suspend fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
|
val loadData = parseJson<LoadData>(data)
|
||||||
|
callback.invoke(
|
||||||
|
ExtractorLink(
|
||||||
|
this.name,
|
||||||
|
loadData.title,
|
||||||
|
loadData.url,
|
||||||
|
"",
|
||||||
|
Qualities.Unknown.value,
|
||||||
|
isM3u8 = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
data class Playlist(
|
||||||
|
val items: List<PlaylistItem> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PlaylistItem(
|
||||||
|
val title: String? = null,
|
||||||
|
val attributes: Map<String, String> = emptyMap(),
|
||||||
|
val headers: Map<String, String> = emptyMap(),
|
||||||
|
val url: String? = null,
|
||||||
|
val userAgent: String? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class IptvPlaylistParser {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse M3U8 string into [Playlist]
|
||||||
|
*
|
||||||
|
* @param content M3U8 content string.
|
||||||
|
* @throws PlaylistParserException if an error occurs.
|
||||||
|
*/
|
||||||
|
fun parseM3U(content: String): Playlist {
|
||||||
|
return parseM3U(content.byteInputStream())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse M3U8 content [InputStream] into [Playlist]
|
||||||
|
*
|
||||||
|
* @param input Stream of input data.
|
||||||
|
* @throws PlaylistParserException if an error occurs.
|
||||||
|
*/
|
||||||
|
@Throws(PlaylistParserException::class)
|
||||||
|
fun parseM3U(input: InputStream): Playlist {
|
||||||
|
val reader = input.bufferedReader()
|
||||||
|
|
||||||
|
if (!reader.readLine().isExtendedM3u()) {
|
||||||
|
throw PlaylistParserException.InvalidHeader()
|
||||||
|
}
|
||||||
|
|
||||||
|
val playlistItems: MutableList<PlaylistItem> = mutableListOf()
|
||||||
|
var currentIndex = 0
|
||||||
|
|
||||||
|
var line: String? = reader.readLine()
|
||||||
|
|
||||||
|
while (line != null) {
|
||||||
|
if (line.isNotEmpty()) {
|
||||||
|
if (line.startsWith(EXT_INF)) {
|
||||||
|
val title = line.getTitle()
|
||||||
|
val attributes = line.getAttributes()
|
||||||
|
playlistItems.add(PlaylistItem(title, attributes))
|
||||||
|
} else if (line.startsWith(EXT_VLC_OPT)) {
|
||||||
|
val item = playlistItems[currentIndex]
|
||||||
|
val userAgent = line.getTagValue("http-user-agent")
|
||||||
|
val referrer = line.getTagValue("http-referrer")
|
||||||
|
val headers = if (referrer != null) {
|
||||||
|
item.headers + mapOf("referrer" to referrer)
|
||||||
|
} else item.headers
|
||||||
|
playlistItems[currentIndex] =
|
||||||
|
item.copy(userAgent = userAgent, headers = headers)
|
||||||
|
} else {
|
||||||
|
if (!line.startsWith("#")) {
|
||||||
|
val item = playlistItems[currentIndex]
|
||||||
|
val url = line.getUrl()
|
||||||
|
val userAgent = line.getUrlParameter("user-agent")
|
||||||
|
val referrer = line.getUrlParameter("referer")
|
||||||
|
val urlHeaders = if (referrer != null) {
|
||||||
|
item.headers + mapOf("referrer" to referrer)
|
||||||
|
} else item.headers
|
||||||
|
playlistItems[currentIndex] =
|
||||||
|
item.copy(
|
||||||
|
url = url,
|
||||||
|
headers = item.headers + urlHeaders,
|
||||||
|
userAgent = userAgent
|
||||||
|
)
|
||||||
|
currentIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
line = reader.readLine()
|
||||||
|
}
|
||||||
|
return Playlist(playlistItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace "" (quotes) from given string.
|
||||||
|
*/
|
||||||
|
private fun String.replaceQuotesAndTrim(): String {
|
||||||
|
return replace("\"", "").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if given content is valid M3U8 playlist.
|
||||||
|
*/
|
||||||
|
private fun String.isExtendedM3u(): Boolean = startsWith(EXT_M3U)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get title of media.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* #EXTINF:-1 tvg-id="1234" group-title="Kids" tvg-logo="url/to/logo", Title
|
||||||
|
* ```
|
||||||
|
* Result: Title
|
||||||
|
*/
|
||||||
|
private fun String.getTitle(): String? {
|
||||||
|
return split(",").lastOrNull()?.replaceQuotesAndTrim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get media url.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* https://example.com/sample.m3u8|user-agent="Custom"
|
||||||
|
* ```
|
||||||
|
* Result: https://example.com/sample.m3u8
|
||||||
|
*/
|
||||||
|
private fun String.getUrl(): String? {
|
||||||
|
return split("|").firstOrNull()?.replaceQuotesAndTrim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get url parameters.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* http://192.54.104.122:8080/d/abcdef/video.mp4|User-Agent=Mozilla&Referer=CustomReferrer
|
||||||
|
* ```
|
||||||
|
* Result will be equivalent to kotlin map:
|
||||||
|
* ```Kotlin
|
||||||
|
* mapOf(
|
||||||
|
* "User-Agent" to "Mozilla",
|
||||||
|
* "Referer" to "CustomReferrer"
|
||||||
|
* )
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private fun String.getUrlParameters(): Map<String, String> {
|
||||||
|
val urlRegex = Regex("^(.*)\\|", RegexOption.IGNORE_CASE)
|
||||||
|
val headersString = replace(urlRegex, "").replaceQuotesAndTrim()
|
||||||
|
return headersString.split("&").mapNotNull {
|
||||||
|
val pair = it.split("=")
|
||||||
|
if (pair.size == 2) pair.first() to pair.last() else null
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get url parameter with key.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* http://192.54.104.122:8080/d/abcdef/video.mp4|User-Agent=Mozilla&Referer=CustomReferrer
|
||||||
|
* ```
|
||||||
|
* If given key is `user-agent`, then
|
||||||
|
*
|
||||||
|
* Result: Mozilla
|
||||||
|
*/
|
||||||
|
private fun String.getUrlParameter(key: String): String? {
|
||||||
|
val urlRegex = Regex("^(.*)\\|", RegexOption.IGNORE_CASE)
|
||||||
|
val keyRegex = Regex("$key=(\\w[^&]*)", RegexOption.IGNORE_CASE)
|
||||||
|
val paramsString = replace(urlRegex, "").replaceQuotesAndTrim()
|
||||||
|
return keyRegex.find(paramsString)?.groups?.get(1)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get attributes from `#EXTINF` tag as Map<String, String>.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* #EXTINF:-1 tvg-id="1234" group-title="Kids" tvg-logo="url/to/logo", Title
|
||||||
|
* ```
|
||||||
|
* Result will be equivalent to kotlin map:
|
||||||
|
* ```Kotlin
|
||||||
|
* mapOf(
|
||||||
|
* "tvg-id" to "1234",
|
||||||
|
* "group-title" to "Kids",
|
||||||
|
* "tvg-logo" to "url/to/logo"
|
||||||
|
*)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
private fun String.getAttributes(): Map<String, String> {
|
||||||
|
val extInfRegex = Regex("(#EXTINF:.?[0-9]+)", RegexOption.IGNORE_CASE)
|
||||||
|
val attributesString = replace(extInfRegex, "").replaceQuotesAndTrim().split(",").first()
|
||||||
|
return attributesString.split(Regex("\\s")).mapNotNull {
|
||||||
|
val pair = it.split("=")
|
||||||
|
if (pair.size == 2) pair.first() to pair.last()
|
||||||
|
.replaceQuotesAndTrim() else null
|
||||||
|
}.toMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value from a tag.
|
||||||
|
*
|
||||||
|
* Example:-
|
||||||
|
*
|
||||||
|
* Input:
|
||||||
|
* ```
|
||||||
|
* #EXTVLCOPT:http-referrer=http://example.com/
|
||||||
|
* ```
|
||||||
|
* Result: http://example.com/
|
||||||
|
*/
|
||||||
|
private fun String.getTagValue(key: String): String? {
|
||||||
|
val keyRegex = Regex("$key=(.*)", RegexOption.IGNORE_CASE)
|
||||||
|
return keyRegex.find(this)?.groups?.get(1)?.value?.replaceQuotesAndTrim()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXT_M3U = "#EXTM3U"
|
||||||
|
const val EXT_INF = "#EXTINF"
|
||||||
|
const val EXT_VLC_OPT = "#EXTVLCOPT"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when an error occurs while parsing playlist.
|
||||||
|
*/
|
||||||
|
sealed class PlaylistParserException(message: String) : Exception(message) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown if given file content is not valid.
|
||||||
|
*/
|
||||||
|
class InvalidHeader :
|
||||||
|
PlaylistParserException("Invalid file header. Header doesn't start with #EXTM3U")
|
||||||
|
|
||||||
|
}
|
|
@ -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 TvItalianaProviderPlugin: Plugin() {
|
||||||
|
override fun load(context: Context) {
|
||||||
|
// All providers should be added in this manner. Please don't edit the providers list directly.
|
||||||
|
registerMainAPI(TvItalianaProvider())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue