mirror of
				https://github.com/recloudstream/cloudstream.git
				synced 2024-08-15 01:53:11 +00:00 
			
		
		
		
	Merge pull request #1 from LagradOst/tenshi.moe
Added provider Tenshi.moe
This commit is contained in:
		
						commit
						3583145e42
					
				
					 2 changed files with 252 additions and 1 deletions
				
			
		|  | @ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper | |||
| import com.fasterxml.jackson.module.kotlin.KotlinModule | ||||
| import com.lagradost.cloudstream3.animeproviders.DubbedAnimeProvider | ||||
| import com.lagradost.cloudstream3.animeproviders.ShiroProvider | ||||
| import com.lagradost.cloudstream3.animeproviders.TenshiProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.HDMProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.LookMovieProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider | ||||
|  | @ -31,6 +32,7 @@ object APIHolder { | |||
| 
 | ||||
|     val apis = arrayListOf( | ||||
|         ShiroProvider(), | ||||
|         TenshiProvider(), | ||||
|         MeloMovieProvider(), | ||||
|         DubbedAnimeProvider(), | ||||
|         HDMProvider(), | ||||
|  |  | |||
|  | @ -0,0 +1,249 @@ | |||
| package com.lagradost.cloudstream3.animeproviders | ||||
| 
 | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.fasterxml.jackson.module.kotlin.readValue | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.utils.ExtractorLink | ||||
| import com.lagradost.cloudstream3.utils.getQualityFromName | ||||
| import org.jsoup.Jsoup | ||||
| import org.jsoup.nodes.Document | ||||
| import java.util.* | ||||
| import kotlin.collections.ArrayList | ||||
| import khttp.structures.cookie.CookieJar | ||||
| 
 | ||||
| 
 | ||||
| class TenshiProvider : MainAPI() { | ||||
| 
 | ||||
|     companion object { | ||||
|         var token: String? = null | ||||
|         var cookie: CookieJar? = null | ||||
| 
 | ||||
|         fun getType(t: String): TvType { | ||||
|             if (t.contains("OVA") || t.contains("Special"))  return TvType.ONA | ||||
|             else if (t.contains("Movie")) return TvType.Movie | ||||
|             else return TvType.Anime | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override val mainUrl: String | ||||
|         get() = "https://tenshi.moe" | ||||
|     override val name: String | ||||
|         get() = "Tenshi.moe" | ||||
|     override val hasQuickSearch: Boolean | ||||
|         get() = false | ||||
| 
 | ||||
|     private fun autoLoadToken(): Boolean { | ||||
|         if (token != null) return true | ||||
|         return loadToken() | ||||
|     } | ||||
| 
 | ||||
|     private fun loadToken(): Boolean { | ||||
|         return try { | ||||
|             val response = khttp.get( | ||||
|                 "https://tenshi.moe/", | ||||
|             ) | ||||
|             cookie = response.cookies | ||||
|             val document = Jsoup.parse(response.text) | ||||
|             token = document.selectFirst("""meta[name="csrf-token"]""").attr("content") | ||||
|             token != null | ||||
|         } catch (e: Exception) { | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private fun getIsMovie(type: String, id: Boolean = false): Boolean { | ||||
|         if (!id) return type == "Movie" | ||||
| 
 | ||||
|         val movies = listOf("rrso24fa", "e4hqvtym", "bl5jdbqn", "u4vtznut", "37t6h2r4", "cq4azcrj") | ||||
|         val aniId = type.replace("$mainUrl/anime/", "") | ||||
|         return movies.contains(aniId) | ||||
|     } | ||||
| 
 | ||||
|     private fun getSlug(href: String): String { | ||||
|         return href.replace("$mainUrl/anime/", "") | ||||
|     } | ||||
| 
 | ||||
|     private fun parseSearchPage(soup: Document): ArrayList<SearchResponse> { | ||||
|         val items = soup.select("ul.thumb > li > a") | ||||
|         if (items.isEmpty()) return ArrayList() | ||||
|         val returnValue = ArrayList<SearchResponse>() | ||||
|         for (i in items) { | ||||
|             val href = fixUrl(i.attr("href")) | ||||
|             val img = fixUrl(i.selectFirst("img").attr("src")) | ||||
|             val title = i.attr("title") | ||||
| 
 | ||||
|             returnValue.add( | ||||
|                 if (getIsMovie(href, true)) { | ||||
|                     MovieSearchResponse( | ||||
|                         title, href, getSlug(href), this.name, TvType.Movie, img, null | ||||
|                     ) | ||||
|                 } else { | ||||
|                     AnimeSearchResponse( | ||||
|                         title, href, getSlug(href), | ||||
|                         this.name, TvType.Anime, | ||||
|                         img, null, null, | ||||
|                         EnumSet.of(DubStatus.Subbed), | ||||
|                         null, null | ||||
|                     ) | ||||
|                 } | ||||
|             ) | ||||
|         } | ||||
|         return returnValue | ||||
|     } | ||||
| 
 | ||||
| //    data class TenshiSearchResponse( | ||||
| //        @JsonProperty("url") var url : String, | ||||
| //        @JsonProperty("title") var title : String, | ||||
| //        @JsonProperty("cover") var cover : String, | ||||
| //        @JsonProperty("genre") var genre : String, | ||||
| //        @JsonProperty("year") var year : Int, | ||||
| //        @JsonProperty("type") var type : String, | ||||
| //        @JsonProperty("eps") var eps : String, | ||||
| //        @JsonProperty("cen") var cen : String | ||||
| //    ) | ||||
| 
 | ||||
| //    override fun quickSearch(query: String): ArrayList<SearchResponse>? { | ||||
| //        if (!autoLoadToken()) return quickSearch(query) | ||||
| //        val url = "$mainUrl/anime/search" | ||||
| //        val response = khttp.post( | ||||
| //            url, | ||||
| //            data=mapOf("q" to query), | ||||
| //            headers=mapOf("x-csrf-token" to token, "x-requested-with" to "XMLHttpRequest"), | ||||
| //            cookies = cookie | ||||
| // | ||||
| //        ) | ||||
| // | ||||
| //        val items = mapper.readValue<List<TenshiSearchResponse>>(response.text) | ||||
| // | ||||
| //        if (items.isEmpty()) return ArrayList() | ||||
| // | ||||
| //        val returnValue = ArrayList<SearchResponse>() | ||||
| //        for (i in items) { | ||||
| //            val href = fixUrl(i.url) | ||||
| //            val title = i.title | ||||
| //            val img = fixUrl(i.cover) | ||||
| //            val year = i.year | ||||
| // | ||||
| //            returnValue.add( | ||||
| //                if (getIsMovie(i.type)) { | ||||
| //                    MovieSearchResponse( | ||||
| //                        title, href, getSlug(href), this.name, TvType.Movie, img, year | ||||
| //                    ) | ||||
| //                } else { | ||||
| //                    AnimeSearchResponse( | ||||
| //                        title, href, getSlug(href), this.name, | ||||
| //                        TvType.Anime, img,  year, null, | ||||
| //                        EnumSet.of(DubStatus.Subbed), | ||||
| //                        null, null | ||||
| //                    ) | ||||
| //                } | ||||
| //            ) | ||||
| //        } | ||||
| //        return returnValue | ||||
| //    } | ||||
| 
 | ||||
|     override fun search(query: String): ArrayList<SearchResponse> { | ||||
|         val url = "$mainUrl/anime" | ||||
|         var response = khttp.get(url, params=mapOf("q" to query), cookies=mapOf("loop-view" to "thumb")) | ||||
|         var document = Jsoup.parse(response.text) | ||||
|         val returnValue = parseSearchPage(document) | ||||
| 
 | ||||
|         while (document.select("""a.page-link[rel="next"]""") != null && !document.select("""a.page-link[rel="next"]""").isEmpty()) { | ||||
|             val link = document.select("""a.page-link[rel="next"]""") | ||||
|             if (link != null && !link.isEmpty()) { | ||||
|                 response = khttp.get(link[0].attr("href"), cookies=mapOf("loop-view" to "thumb")) | ||||
|                 document = Jsoup.parse(response.text) | ||||
|                 returnValue.addAll(parseSearchPage(document)) | ||||
|             } else { | ||||
|                 break | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         return returnValue | ||||
|     } | ||||
| 
 | ||||
|     override fun load(slug: String): LoadResponse? { | ||||
|         val url = "$mainUrl/anime/${slug}" | ||||
| 
 | ||||
|         val response = khttp.get(url, timeout = 120.0) | ||||
|         val document = Jsoup.parse(response.text) | ||||
| 
 | ||||
|         val englishTitle = document.selectFirst("span.value > span[title=\"English\"]")?.parent()?.text()?.trim() | ||||
|         val japaneseTitle = document.selectFirst("span.value > span[title=\"Japanese\"]")?.parent()?.text()?.trim() | ||||
|         val canonicalTitle = document.selectFirst("header.entry-header > h1.mb-3").text().trim() | ||||
| 
 | ||||
|         val isDubbed = false | ||||
|         val episodeNodes = document.select("li[class*=\"episode\"] > a") | ||||
| 
 | ||||
|         val episodes = ArrayList<AnimeEpisode>(episodeNodes?.map { AnimeEpisode(it.attr("href")) } | ||||
|             ?: ArrayList<AnimeEpisode>()) | ||||
| 
 | ||||
|         val statusText = document.selectFirst("li.status > .value").text().trim() | ||||
|         val status = when (statusText) { | ||||
|             "Ongoing" -> ShowStatus.Ongoing | ||||
|             "Completed" -> ShowStatus.Completed | ||||
|             else -> null | ||||
|         } | ||||
|         val yearText = document.selectFirst("li.release-date .value").text() | ||||
|         val pattern = "(\\d{4})".toRegex() | ||||
|         val (year) = pattern.find(yearText)!!.destructured | ||||
| 
 | ||||
|         val poster = document.selectFirst("img.cover-image").attr("src") | ||||
|         val type = document.selectFirst("a[href*=\"https://tenshi.moe/type/\"]").text().trim() | ||||
| 
 | ||||
|         val synopsis = document.selectFirst(".entry-description > .card-body").text().trim() | ||||
|         val genre = document.select("li.genre.meta-data > span.value").map { it.text().trim() } | ||||
| 
 | ||||
|         val synonyms = document.select("li.synonym.meta-data > div.info-box > span.value").map { it?.text()?.trim().toString() } | ||||
| 
 | ||||
|         return AnimeLoadResponse( | ||||
|             englishTitle, | ||||
|             japaneseTitle, | ||||
|             canonicalTitle, | ||||
|             "$mainUrl/anime/${slug}", | ||||
|             this.name, | ||||
|             TenshiProvider.getType(type ?: ""), | ||||
|             poster, | ||||
|             year.toIntOrNull(), | ||||
|             null, | ||||
|             episodes, | ||||
|             status, | ||||
|             synopsis, | ||||
|             ArrayList(genre) ?: ArrayList(), | ||||
|             ArrayList(synonyms), | ||||
|             null, null, | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         val response = khttp.get(data) | ||||
|         val src = Jsoup.parse(response.text).selectFirst(".embed-responsive > iframe").attr("src") | ||||
|         val mp4moe = Jsoup.parse(khttp.get(src).text).selectFirst("video#player") | ||||
| 
 | ||||
|         for (source in mp4moe.select("source")) { | ||||
|             try { | ||||
|                 val quality = source.attr("title") | ||||
|                 val link = source.attr("src") | ||||
| 
 | ||||
|                 callback.invoke( | ||||
|                     ExtractorLink( | ||||
|                         this.name, | ||||
|                         "${this.name} $quality" + if (quality.endsWith('p')) "" else 'p', | ||||
|                         fixUrl(link), | ||||
|                         this.mainUrl, | ||||
|                         getQualityFromName(quality) | ||||
|                     ) | ||||
|                 ) | ||||
|             } catch (e: Exception) { | ||||
|                 //IDK | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue