forked from recloudstream/cloudstream
		
	add watchasian provider. (#410)
* add watchasian provider. enable downloading on dramasee * applied pr changes. * shorthand for cleaning title
This commit is contained in:
		
							parent
							
								
									4215c094ff
								
							
						
					
					
						commit
						9dc186335e
					
				
					 3 changed files with 201 additions and 1 deletions
				
			
		|  | @ -67,7 +67,8 @@ object APIHolder { | ||||||
|         PinoyHDXyzProvider(), |         PinoyHDXyzProvider(), | ||||||
|         PinoyMoviesEsProvider(), |         PinoyMoviesEsProvider(), | ||||||
|         TrailersTwoProvider(), |         TrailersTwoProvider(), | ||||||
|         DramaSeeProvider() |         DramaSeeProvider(), | ||||||
|  |         WatchAsianProvider() | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     val restrictedApis = arrayListOf( |     val restrictedApis = arrayListOf( | ||||||
|  |  | ||||||
|  | @ -13,6 +13,7 @@ class DramaSeeProvider : MainAPI() { | ||||||
|     override val hasQuickSearch = false |     override val hasQuickSearch = false | ||||||
|     override val hasMainPage = true |     override val hasMainPage = true | ||||||
|     override val hasChromecastSupport = false |     override val hasChromecastSupport = false | ||||||
|  |     override val hasDownloadSupport = true | ||||||
|     override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) |     override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) | ||||||
| 
 | 
 | ||||||
|     override fun getMainPage(): HomePageResponse { |     override fun getMainPage(): HomePageResponse { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,198 @@ | ||||||
|  | package com.lagradost.cloudstream3.movieproviders | ||||||
|  | 
 | ||||||
|  | import android.util.Log | ||||||
|  | import com.fasterxml.jackson.module.kotlin.readValue | ||||||
|  | import com.lagradost.cloudstream3.* | ||||||
|  | import com.lagradost.cloudstream3.extractors.* | ||||||
|  | import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||||
|  | import com.lagradost.cloudstream3.utils.ExtractorLink | ||||||
|  | import com.lagradost.cloudstream3.utils.loadExtractor | ||||||
|  | 
 | ||||||
|  | class WatchAsianProvider : MainAPI() { | ||||||
|  |     override val mainUrl = "https://watchasian.sh" | ||||||
|  |     override val name = "WatchAsian" | ||||||
|  |     override val hasQuickSearch = false | ||||||
|  |     override val hasMainPage = true | ||||||
|  |     override val hasChromecastSupport = false | ||||||
|  |     override val hasDownloadSupport = true | ||||||
|  |     override val supportedTypes = setOf(TvType.TvSeries, TvType.Movie) | ||||||
|  | 
 | ||||||
|  |     override fun getMainPage(): HomePageResponse { | ||||||
|  |         val headers = mapOf("X-Requested-By" to mainUrl) | ||||||
|  |         val doc = app.get(mainUrl, headers = headers).document | ||||||
|  |         val rowPair = mutableListOf<Pair<String, String>>() | ||||||
|  |         doc.select("div.block-tab")?.forEach { | ||||||
|  |             it?.select("ul.tab > li")?.mapNotNull { row -> | ||||||
|  |                 val link = row?.attr("data-tab") ?: return@mapNotNull null | ||||||
|  |                 val title = row.text() ?: return@mapNotNull null | ||||||
|  |                 Pair(title, link) | ||||||
|  |             }?.let { it1 -> | ||||||
|  |                 rowPair.addAll( | ||||||
|  |                     it1 | ||||||
|  |                 ) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return HomePageResponse( | ||||||
|  |             rowPair.mapNotNull { row -> | ||||||
|  |             val main = (doc.select("div.tab-content.${row.second}") | ||||||
|  |                 ?: doc.select("div.tab-content.${row.second}.selected")) ?: return@mapNotNull null | ||||||
|  | 
 | ||||||
|  |             val title = row.first | ||||||
|  |             val inner = main.select("li") ?: return@mapNotNull null | ||||||
|  | 
 | ||||||
|  |             HomePageList( | ||||||
|  |                 title, | ||||||
|  |                 inner.map { | ||||||
|  |                 // Get inner div from article | ||||||
|  |                 val innerBody = it?.selectFirst("a") | ||||||
|  |                 // Fetch details | ||||||
|  |                 val link = fixUrlNull(innerBody?.attr("href")) ?: return@map null | ||||||
|  |                 val image = fixUrlNull(innerBody?.select("img")?.attr("data-original")) ?: "" | ||||||
|  |                 val name = (innerBody?.selectFirst("h3.title")?.text() ?: innerBody?.text())?: "<Untitled>" | ||||||
|  |                 //Log.i(this.name, "Result => (innerBody, image) ${innerBody} / ${image}") | ||||||
|  |                 MovieSearchResponse( | ||||||
|  |                     name, | ||||||
|  |                     link, | ||||||
|  |                     this.name, | ||||||
|  |                     TvType.TvSeries, | ||||||
|  |                     image, | ||||||
|  |                     year = null, | ||||||
|  |                     id = null, | ||||||
|  |                 ) | ||||||
|  |                 }.filterNotNull().distinctBy { c -> c.url }) | ||||||
|  |             }.filter { a -> a.list.isNotEmpty() } | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun search(query: String): List<SearchResponse> { | ||||||
|  |         val url = "$mainUrl/search?type=movies&keyword=$query" | ||||||
|  |         val document = app.get(url).document.getElementsByTag("body") | ||||||
|  |                 .select("div.block.tab-container > div > ul > li") ?: return listOf() | ||||||
|  | 
 | ||||||
|  |         return document.mapNotNull { | ||||||
|  |             val innerA = it?.selectFirst("a") ?: return@mapNotNull null | ||||||
|  |             val link = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null | ||||||
|  |             val title = it.select("h3.title")?.text() ?: return@mapNotNull null | ||||||
|  |             if (title.isEmpty()) { return@mapNotNull null } | ||||||
|  |             val year = null | ||||||
|  |             val imgsrc = innerA.select("img")?.attr("data-original") ?: return@mapNotNull null | ||||||
|  |             val image = fixUrlNull(imgsrc) | ||||||
|  |             //Log.i(this.name, "Result => (img movie) $title / $link") | ||||||
|  |             MovieSearchResponse( | ||||||
|  |                 title, | ||||||
|  |                 link, | ||||||
|  |                 this.name, | ||||||
|  |                 TvType.Movie, | ||||||
|  |                 image, | ||||||
|  |                 year | ||||||
|  |             ) | ||||||
|  |         }.distinctBy { a -> a.url } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun load(url: String): LoadResponse { | ||||||
|  |         val body = app.get(url).document | ||||||
|  |         // Declare vars | ||||||
|  |         val isDramaDetail = url.contains("/drama-detail/") | ||||||
|  |         var poster = "" | ||||||
|  |         var title = "" | ||||||
|  |         var descript : String? = null | ||||||
|  |         var year : Int? = null | ||||||
|  |         if (isDramaDetail) { | ||||||
|  |             val main = body.select("div.details") | ||||||
|  |             val inner = main?.select("div.info") | ||||||
|  |             // Video details | ||||||
|  |             poster = fixUrlNull(main?.select("div.img > img")?.attr("src")) ?: "" | ||||||
|  |             //Log.i(this.name, "Result => (imgLinkCode) ${imgLinkCode}") | ||||||
|  |             title = inner?.select("h1")?.firstOrNull()?.text() ?: "" | ||||||
|  |             year = if (title.length > 5) { | ||||||
|  |                 title.replace(")", "").replace("(", "").substring(title.length - 5) | ||||||
|  |                     .trim().trimEnd(')').toIntOrNull() | ||||||
|  |             } else { | ||||||
|  |                 null | ||||||
|  |             } | ||||||
|  |             //Log.i(this.name, "Result => (year) ${title.substring(title.length - 5)}") | ||||||
|  |             descript = inner?.text() | ||||||
|  |         } else { | ||||||
|  |             poster = body.select("meta[itemprop=\"image\"]")?.attr("content") ?: "" | ||||||
|  |             title = body.selectFirst("div.block.watch-drama")?.selectFirst("h1") | ||||||
|  |                 ?.text() ?: "" | ||||||
|  |             year = null | ||||||
|  |             descript = body.select("meta[name=\"description\"]")?.attr("content") | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Episodes Links | ||||||
|  |         //Log.i(this.name, "Result => (all eps) ${body.select("ul.list-episode-item-2.all-episode > li")}") | ||||||
|  |         val episodeList = body.select("ul.list-episode-item-2.all-episode > li")?.mapNotNull { ep -> | ||||||
|  |             //Log.i(this.name, "Result => (epA) ${ep.select("a")}") | ||||||
|  |             val innerA = ep.select("a") ?: return@mapNotNull null | ||||||
|  |             //Log.i(this.name, "Result => (innerA) ${fixUrlNull(innerA.attr("href"))}") | ||||||
|  |             val epLink = fixUrlNull(innerA.attr("href")) ?: return@mapNotNull null | ||||||
|  | 
 | ||||||
|  |             val regex = "(?<=episode-).*?(?=.html)".toRegex() | ||||||
|  |             val count = regex.find(epLink, mainUrl.length)?.value?.toIntOrNull() ?: 0 | ||||||
|  |             //Log.i(this.name, "Result => $epLink (regexYear) ${count}") | ||||||
|  |             TvSeriesEpisode( | ||||||
|  |                 name = null, | ||||||
|  |                 season = null, | ||||||
|  |                 episode = count, | ||||||
|  |                 data = epLink, | ||||||
|  |                 posterUrl = poster, | ||||||
|  |                 date = null | ||||||
|  |             ) | ||||||
|  |         } ?: listOf() | ||||||
|  |         //If there's only 1 episode, consider it a movie. | ||||||
|  |         if (episodeList.size == 1) { | ||||||
|  |             //Clean title | ||||||
|  |             title = title.removeSuffix("Episode 1") | ||||||
|  |             val streamlink = getServerLinks(episodeList[0].data) | ||||||
|  |             //Log.i(this.name, "Result => (streamlink) $streamlink") | ||||||
|  |             return MovieLoadResponse(title, url, this.name, TvType.Movie, streamlink, poster, year, descript, null, null) | ||||||
|  |         } | ||||||
|  |         return TvSeriesLoadResponse( | ||||||
|  |             title, | ||||||
|  |             url, | ||||||
|  |             this.name, | ||||||
|  |             TvType.TvSeries, | ||||||
|  |             episodeList, | ||||||
|  |             poster, | ||||||
|  |             year, | ||||||
|  |             descript, | ||||||
|  |             null, | ||||||
|  |             null, | ||||||
|  |             null | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     override fun loadLinks( | ||||||
|  |         data: String, | ||||||
|  |         isCasting: Boolean, | ||||||
|  |         subtitleCallback: (SubtitleFile) -> Unit, | ||||||
|  |         callback: (ExtractorLink) -> Unit | ||||||
|  |     ): Boolean { | ||||||
|  |         if (data == "about:blank") return false | ||||||
|  |         if (data == "[]") return false | ||||||
|  |         if (data.isEmpty()) return false | ||||||
|  |         val links = if (data.startsWith(mainUrl)) { | ||||||
|  |             getServerLinks(data) | ||||||
|  |         } else { data } | ||||||
|  |         mapper.readValue<List<String>>(links) | ||||||
|  |             .forEach { item -> | ||||||
|  |                 var url = item.trim() | ||||||
|  |                 if (url.startsWith("//")) { | ||||||
|  |                     url = "https:$url" | ||||||
|  |                 } | ||||||
|  |                 //Log.i(this.name, "Result => (url) $url") | ||||||
|  |                 loadExtractor(url, mainUrl, callback) | ||||||
|  |             } | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private fun getServerLinks(url: String) : String { | ||||||
|  |         val moviedoc = app.get(url, referer = mainUrl).document | ||||||
|  |         return moviedoc.select("div.anime_muti_link > ul > li") | ||||||
|  |             ?.mapNotNull { | ||||||
|  |                 fixUrlNull(it?.attr("data-video")) ?: return@mapNotNull null | ||||||
|  |             }?.toJson() ?: "" | ||||||
|  |     } | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue