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…
	
	Add table
		Add a link
		
	
		Reference in a new issue