forked from recloudstream/cloudstream
		
	Merge remote-tracking branch 'origin/master'
This commit is contained in:
		
						commit
						f3e7163436
					
				
					 19 changed files with 395 additions and 34 deletions
				
			
		|  | @ -10,7 +10,6 @@ import com.fasterxml.jackson.annotation.JsonProperty | |||
| import com.fasterxml.jackson.databind.DeserializationFeature | ||||
| import com.fasterxml.jackson.databind.json.JsonMapper | ||||
| import com.fasterxml.jackson.module.kotlin.KotlinModule | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addMalId | ||||
| import com.lagradost.cloudstream3.animeproviders.* | ||||
| import com.lagradost.cloudstream3.metaproviders.CrossTmdbProvider | ||||
| import com.lagradost.cloudstream3.movieproviders.* | ||||
|  | @ -93,6 +92,7 @@ object APIHolder { | |||
|             TantifilmProvider(), | ||||
|             CineblogProvider(), | ||||
|             AltadefinizioneProvider(), | ||||
|             FilmpertuttiProvider(), | ||||
|             HDMovie5(), | ||||
|             RebahinProvider(), | ||||
|             LayarKacaProvider(), | ||||
|  | @ -899,11 +899,11 @@ interface LoadResponse { | |||
|             this.actors = actors?.map { actor -> ActorData(actor) } | ||||
|         } | ||||
| 
 | ||||
|         fun LoadResponse.getMalId() : String? { | ||||
|         fun LoadResponse.getMalId(): String? { | ||||
|             return this.syncData[malIdPrefix] | ||||
|         } | ||||
| 
 | ||||
|         fun LoadResponse.getAniListId() : String? { | ||||
|         fun LoadResponse.getAniListId(): String? { | ||||
|             return this.syncData[aniListIdPrefix] | ||||
|         } | ||||
| 
 | ||||
|  | @ -1004,7 +1004,7 @@ interface LoadResponse { | |||
| 
 | ||||
| fun LoadResponse?.isEpisodeBased(): Boolean { | ||||
|     if (this == null) return false | ||||
|     return (this is AnimeLoadResponse || this is TvSeriesLoadResponse) && this.type.isEpisodeBased() | ||||
|     return this is EpisodeResponse && this.type.isEpisodeBased() | ||||
| } | ||||
| 
 | ||||
| fun LoadResponse?.isAnimeBased(): Boolean { | ||||
|  | @ -1017,6 +1017,17 @@ fun TvType?.isEpisodeBased(): Boolean { | |||
|     return (this == TvType.TvSeries || this == TvType.Anime) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| data class NextAiring( | ||||
|     val episode: Int, | ||||
|     val unixTime: Long, | ||||
| ) | ||||
| 
 | ||||
| interface EpisodeResponse { | ||||
|     var showStatus: ShowStatus? | ||||
|     var nextAiring: NextAiring? | ||||
| } | ||||
| 
 | ||||
| data class TorrentLoadResponse( | ||||
|     override var name: String, | ||||
|     override var url: String, | ||||
|  | @ -1050,7 +1061,7 @@ data class AnimeLoadResponse( | |||
|     override var year: Int? = null, | ||||
| 
 | ||||
|     var episodes: MutableMap<DubStatus, List<Episode>> = mutableMapOf(), | ||||
|     var showStatus: ShowStatus? = null, | ||||
|     override var showStatus: ShowStatus? = null, | ||||
| 
 | ||||
|     override var plot: String? = null, | ||||
|     override var tags: List<String>? = null, | ||||
|  | @ -1064,7 +1075,8 @@ data class AnimeLoadResponse( | |||
|     override var comingSoon: Boolean = false, | ||||
|     override var syncData: MutableMap<String, String> = mutableMapOf(), | ||||
|     override var posterHeaders: Map<String, String>? = null, | ||||
| ) : LoadResponse | ||||
|     override var nextAiring: NextAiring? = null, | ||||
| ) : LoadResponse, EpisodeResponse | ||||
| 
 | ||||
| fun AnimeLoadResponse.addEpisodes(status: DubStatus, episodes: List<Episode>?) { | ||||
|     if (episodes == null) return | ||||
|  | @ -1222,7 +1234,7 @@ data class TvSeriesLoadResponse( | |||
|     override var year: Int? = null, | ||||
|     override var plot: String? = null, | ||||
| 
 | ||||
|     var showStatus: ShowStatus? = null, | ||||
|     override var showStatus: ShowStatus? = null, | ||||
|     override var rating: Int? = null, | ||||
|     override var tags: List<String>? = null, | ||||
|     override var duration: Int? = null, | ||||
|  | @ -1232,7 +1244,8 @@ data class TvSeriesLoadResponse( | |||
|     override var comingSoon: Boolean = false, | ||||
|     override var syncData: MutableMap<String, String> = mutableMapOf(), | ||||
|     override var posterHeaders: Map<String, String>? = null, | ||||
| ) : LoadResponse | ||||
|     override var nextAiring: NextAiring? = null, | ||||
| ) : LoadResponse, EpisodeResponse | ||||
| 
 | ||||
| suspend fun MainAPI.newTvSeriesLoadResponse( | ||||
|     name: String, | ||||
|  |  | |||
|  | @ -14,6 +14,9 @@ class DoodCxExtractor : DoodLaExtractor() { | |||
| class DoodShExtractor : DoodLaExtractor() { | ||||
|     override var mainUrl = "https://dood.sh" | ||||
| } | ||||
| class DoodWatchExtractor : DoodLaExtractor() { | ||||
|     override var mainUrl = "https://dood.watch" | ||||
| } | ||||
| 
 | ||||
| class DoodPmExtractor : DoodLaExtractor() { | ||||
|     override var mainUrl = "https://dood.pm" | ||||
|  |  | |||
|  | @ -10,6 +10,9 @@ class MixDropBz : MixDrop(){ | |||
| class MixDropCh : MixDrop(){ | ||||
|     override var mainUrl = "https://mixdrop.ch" | ||||
| } | ||||
| class MixDropTo : MixDrop(){ | ||||
|     override var mainUrl = "https://mixdrop.to" | ||||
| } | ||||
| 
 | ||||
| open class MixDrop : ExtractorApi() { | ||||
|     override var name = "MixDrop" | ||||
|  |  | |||
|  | @ -114,7 +114,7 @@ class AltadefinizioneProvider : MainAPI() { | |||
| 
 | ||||
|         val tags: List<String> = document.select("#details > li:nth-child(1) > a").map { it.text() } | ||||
| 
 | ||||
|         val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")!!.attr("src") | ||||
|         val trailerurl = document.selectFirst("#showtrailer > div > div > iframe")?.attr("src") | ||||
| 
 | ||||
|         return newMovieLoadResponse( | ||||
|                 title, | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ import com.lagradost.cloudstream3.mvvm.logError | |||
| 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" | ||||
|  | @ -28,6 +29,7 @@ class CineblogProvider : MainAPI() { | |||
|                 val home = soup.select("article.item.movies").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, | ||||
|  | @ -36,6 +38,7 @@ class CineblogProvider : MainAPI() { | |||
|                         it.selectFirst("img")!!.attr("src"), | ||||
|                         null, | ||||
|                         null, | ||||
|                         quality = quality | ||||
|                     ) | ||||
|                 } | ||||
| 
 | ||||
|  | @ -82,8 +85,7 @@ class CineblogProvider : MainAPI() { | |||
|                 href, | ||||
|                 this.name, | ||||
|                 TvType.Movie, | ||||
|                 poster, | ||||
|                 null | ||||
|                 poster | ||||
|             ) | ||||
| 
 | ||||
|         } | ||||
|  | @ -96,7 +98,6 @@ class CineblogProvider : MainAPI() { | |||
|         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) { | ||||
|  | @ -114,8 +115,7 @@ class CineblogProvider : MainAPI() { | |||
|                 href, | ||||
|                 this.name, | ||||
|                 TvType.Movie, | ||||
|                 posterUrl, | ||||
|                 null | ||||
|                 posterUrl | ||||
|             ) | ||||
| 
 | ||||
|         } | ||||
|  |  | |||
|  | @ -0,0 +1,240 @@ | |||
| package com.lagradost.cloudstream3.movieproviders | ||||
| import androidx.core.text.parseAsHtml | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.mvvm.logError | ||||
| import com.lagradost.cloudstream3.app | ||||
| import com.lagradost.cloudstream3.utils.* | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | ||||
| import com.lagradost.cloudstream3.LoadResponse.Companion.addRating | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.toJson | ||||
| import com.lagradost.cloudstream3.utils.AppUtils.tryParseJson | ||||
| import com.lagradost.nicehttp.NiceResponse | ||||
| import org.jsoup.nodes.Element | ||||
| 
 | ||||
| 
 | ||||
| class FilmpertuttiProvider : MainAPI() { | ||||
|     override var lang = "it" | ||||
|     override var mainUrl = "https://www.filmpertutti.buzz" | ||||
|     override var name = "Filmpertutti" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|     override val supportedTypes = setOf( | ||||
|         TvType.Movie, | ||||
|         TvType.TvSeries | ||||
|     ) | ||||
| 
 | ||||
|     override suspend fun getMainPage(): HomePageResponse { | ||||
|         val items = ArrayList<HomePageList>() | ||||
|         val urls = listOf( | ||||
|             Pair("$mainUrl/category/serie-tv/", "Serie Tv"), | ||||
|             Pair("$mainUrl/category/film/azione/", "Azione"), | ||||
|             Pair("$mainUrl/category/film/avventura/", "Avventura"), | ||||
|         ) | ||||
|         for ((url, name) in urls) { | ||||
|             try { | ||||
|                 val soup = app.get(url).document | ||||
|                 val home = soup.select("ul.posts > li").map { | ||||
|                     val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[") | ||||
|                     val link = it.selectFirst("a")!!.attr("href") | ||||
|                     val image = it.selectFirst("a")!!.attr("data-thumbnail") | ||||
|                     val qualitydata = it.selectFirst("div.hd") | ||||
|                     val quality = if (qualitydata!= null) { | ||||
|                         getQualityFromString(qualitydata?.text()) | ||||
|                     } | ||||
|                     else { | ||||
|                         null | ||||
|                     } | ||||
|                     newTvSeriesSearchResponse( | ||||
|                         title, | ||||
|                         link) { | ||||
|                         this.posterUrl = image | ||||
|                         this.quality = quality | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 items.add(HomePageList(name, home)) | ||||
|             } catch (e: Exception) { | ||||
|                 logError(e) | ||||
|             } | ||||
|         } | ||||
|         if (items.size <= 0) throw ErrorLoadingException() | ||||
|         return HomePageResponse(items) | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun search(query: String): List<SearchResponse> { | ||||
|         val queryformatted = query.replace(" ", "+") | ||||
|         val url = "$mainUrl/?s=$queryformatted" | ||||
|         val doc = app.get(url).document | ||||
|         return doc.select("ul.posts > li").map { | ||||
|             val title = it.selectFirst("div.title")!!.text().substringBeforeLast("(").substringBeforeLast("[") | ||||
|             val link = it.selectFirst("a")!!.attr("href") | ||||
|             val image = it.selectFirst("a")!!.attr("data-thumbnail") | ||||
|             val quality = getQualityFromString(it.selectFirst("div.hd")?.text()) | ||||
| 
 | ||||
|             MovieSearchResponse( | ||||
|                 title, | ||||
|                 link, | ||||
|                 this.name, | ||||
|                 quality = quality, | ||||
|                 posterUrl = image | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     override suspend fun load(url: String): LoadResponse { | ||||
|         val document = app.get(url).document | ||||
|         val type = | ||||
|             if (document.selectFirst("a.taxonomy.category")!!.attr("href").contains("serie-tv") | ||||
|                     .not() | ||||
|             ) TvType.Movie else TvType.TvSeries | ||||
|         val title = document.selectFirst("#content > h1")!!.text().substringBeforeLast("(") | ||||
|             .substringBeforeLast("[") | ||||
| 
 | ||||
|         val description = document.selectFirst("i.fa.fa-file-text-o.fa-fw")?.parent()?.nextSibling()?.toString()?.parseAsHtml().toString() | ||||
| 
 | ||||
| 
 | ||||
|         val rating = document.selectFirst("div.rating > div.value")?.text() | ||||
| 
 | ||||
|         val year = | ||||
|             document.selectFirst("#content > h1")?.text()?.substringAfterLast("(")?.filter { it.isDigit() }?.toIntOrNull() ?: | ||||
|             description.substringAfter("trasmessa nel").take(6).filter { it.isDigit() }.toIntOrNull() ?: | ||||
|             (document.selectFirst("i.fa.fa-calendar.fa-fw")?.parent()?.nextSibling() as Element?)?.text()?.substringAfterLast(" ")?.filter { it.isDigit() }?.toIntOrNull() | ||||
| 
 | ||||
| 
 | ||||
|         val poster = document.selectFirst("div.meta > div > img")?.attr("data-src") | ||||
| 
 | ||||
| 
 | ||||
|         val trailerurl = document.selectFirst("div.youtube-player")?.attr("data-id")?.let{ urldata-> | ||||
|             "https://www.youtube.com/watch?v=$urldata" | ||||
|         } | ||||
| 
 | ||||
|         if (type == TvType.TvSeries) { | ||||
| 
 | ||||
|             val episodeList = ArrayList<Episode>() | ||||
|             document.select("div.accordion-item").filter{it.selectFirst("#season > ul > li.s_title > span")!!.text().isNotEmpty()}.map { element -> | ||||
|                 val season = | ||||
|                     element.selectFirst("#season > ul > li.s_title > span")!!.text().toInt() | ||||
|                 element.select("div.episode-wrap").map { episode -> | ||||
|                     val href = | ||||
|                         episode.select("#links > div > div > table > tbody:nth-child(2) > tr") | ||||
|                             .map { it.selectFirst("a")!!.attr("href") }.toJson() | ||||
|                     val epNum = episode.selectFirst("li.season-no")!!.text().substringAfter("x") | ||||
|                         .filter { it.isDigit() }.toIntOrNull() | ||||
|                     val epTitle = episode.selectFirst("li.other_link > a")?.text() | ||||
| 
 | ||||
|                     val posterUrl = episode.selectFirst("figure > img")?.attr("data-src") | ||||
|                     episodeList.add( | ||||
|                         Episode( | ||||
|                             href, | ||||
|                             epTitle, | ||||
|                             season, | ||||
|                             epNum, | ||||
|                             posterUrl, | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|             return newTvSeriesLoadResponse( | ||||
|                 title, | ||||
|                 url, type, episodeList | ||||
|             ) { | ||||
|                 this.posterUrl = poster | ||||
|                 this.year = year | ||||
|                 this.plot = description | ||||
|                 addRating(rating) | ||||
|                 addTrailer(trailerurl) | ||||
|             } | ||||
|         } else { | ||||
| 
 | ||||
|             val urls0 = document.select("div.embed-player") | ||||
|             val urls = if (urls0.isNotEmpty()){ | ||||
|                     urls0.map { it.attr("data-id") }.toJson() | ||||
|                 } | ||||
|             else{ document.select("#info > ul > li ").mapNotNull { it.selectFirst("a")?.attr("href") }.toJson() } | ||||
| 
 | ||||
|             return newMovieLoadResponse( | ||||
|                 title, | ||||
|                 url, | ||||
|                 type, | ||||
|                 urls | ||||
|             ) { | ||||
|                 posterUrl = fixUrlNull(poster) | ||||
|                 this.year = year | ||||
|                 this.plot = description | ||||
|                 addRating(rating) | ||||
|                 addTrailer(trailerurl) | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| // to be updated when UnshortenUrl is ready | ||||
|     suspend fun unshorten_linkup(uri: String): String { | ||||
|         var r: NiceResponse? = null | ||||
|         var uri = uri | ||||
|         when{ | ||||
|             uri.contains("/tv/") -> uri = uri.replace("/tv/", "/tva/") | ||||
|             uri.contains("delta") -> uri = uri.replace("/delta/", "/adelta/") | ||||
|             (uri.contains("/ga/") || uri.contains("/ga2/")) -> uri = base64Decode(uri.split('/').last()).trim() | ||||
|             uri.contains("/speedx/") -> uri = uri.replace("http://linkup.pro/speedx", "http://speedvideo.net") | ||||
|             else -> { | ||||
|                 r = app.get(uri, allowRedirects = true) | ||||
|                 uri = r.url | ||||
|                 val link = | ||||
|                     Regex("<iframe[^<>]*src=\\'([^'>]*)\\'[^<>]*>").find(r.text)?.value ?: | ||||
|                     Regex("""action="(?:[^/]+.*?/[^/]+/([a-zA-Z0-9_]+))">""").find(r.text)?.value ?: | ||||
|                     Regex("""href","((.|\\n)*?)"""").findAll(r.text).elementAtOrNull(1)?.groupValues?.get(1) | ||||
| 
 | ||||
|                 if (link!=null) { | ||||
|                     uri = link | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         val short = Regex("""^https?://.*?(https?://.*)""").find(uri)?.value | ||||
|         if (short!=null){ | ||||
|             uri = short | ||||
|         } | ||||
|         if (r==null){ | ||||
|             r = app.get( | ||||
|                 uri, | ||||
|                 allowRedirects = false) | ||||
|             if (r.headers["location"]!= null){ | ||||
|                 uri = r.headers["location"].toString() | ||||
|             } | ||||
|         } | ||||
|         if (uri.contains("snip.")) { | ||||
|             if (uri.contains("out_generator")) { | ||||
|                 uri = Regex("url=(.*)\$").find(uri)!!.value | ||||
|             } | ||||
|             else if (uri.contains("/decode/")) { | ||||
|                 uri = app.get(uri, allowRedirects = true).url | ||||
|             } | ||||
|         } | ||||
|         return uri | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     override suspend fun loadLinks( | ||||
|         data: String, | ||||
|         isCasting: Boolean, | ||||
|         subtitleCallback: (SubtitleFile) -> Unit, | ||||
|         callback: (ExtractorLink) -> Unit | ||||
|     ): Boolean { | ||||
|         tryParseJson<List<String>>(data)?.forEach { id -> | ||||
|             if (id.contains("buckler")){ | ||||
|                 val id2 = unshorten_linkup(id).trim().replace("/v/","/e/").replace("/f/","/e/") | ||||
|                 loadExtractor(id2, data, callback) | ||||
|             } | ||||
|             else if (id.contains("isecure")){ | ||||
|                 val doc1 = app.get(id).document | ||||
|                 val id2 = doc1.selectFirst("iframe")!!.attr("src") | ||||
|                 loadExtractor(id2, data, callback) | ||||
|             } | ||||
|             else{ | ||||
|                 loadExtractor(id, data, callback) | ||||
|             } | ||||
|         } | ||||
|         return true | ||||
|     } | ||||
| } | ||||
|  | @ -9,7 +9,7 @@ import com.lagradost.cloudstream3.LoadResponse.Companion.addTrailer | |||
| 
 | ||||
| class TantifilmProvider : MainAPI() { | ||||
|     override var lang = "it" | ||||
|     override var mainUrl = "https://www.tantifilm.rodeo" | ||||
|     override var mainUrl = "https://www.tantifilm.pics" | ||||
|     override var name = "Tantifilm" | ||||
|     override val hasMainPage = true | ||||
|     override val hasChromecastSupport = true | ||||
|  |  | |||
|  | @ -36,11 +36,6 @@ interface SyncAPI : OAuth2API { | |||
|         override var id: Int? = null, | ||||
|     ) : SearchResponse | ||||
| 
 | ||||
|     data class SyncNextAiring( | ||||
|         val episode: Int, | ||||
|         val unixTime: Long, | ||||
|     ) | ||||
| 
 | ||||
|     data class SyncStatus( | ||||
|         val status: Int, | ||||
|         /** 1-10 */ | ||||
|  | @ -63,7 +58,7 @@ interface SyncAPI : OAuth2API { | |||
|         var duration: Int? = null, | ||||
|         var synopsis: String? = null, | ||||
|         var airStatus: ShowStatus? = null, | ||||
|         var nextAiring: SyncNextAiring? = null, | ||||
|         var nextAiring: NextAiring? = null, | ||||
|         var studio: List<String>? = null, | ||||
|         var genres: List<String>? = null, | ||||
|         var synonyms: List<String>? = null, | ||||
|  |  | |||
|  | @ -98,7 +98,7 @@ class AniListApi(index: Int) : AccountManager(index), SyncAPI { | |||
|         return SyncAPI.SyncResult( | ||||
|             season.id.toString(), | ||||
|             nextAiring = season.nextAiringEpisode?.let { | ||||
|                 SyncAPI.SyncNextAiring( | ||||
|                 NextAiring( | ||||
|                     it.episode ?: return@let null, | ||||
|                     (it.timeUntilAiring ?: return@let null) + unixTime | ||||
|                 ) | ||||
|  |  | |||
|  | @ -300,14 +300,14 @@ class CS3IPlayer : IPlayer { | |||
| 
 | ||||
|         saveData() | ||||
|         exoPlayer?.pause() | ||||
|         releasePlayer() | ||||
|         //releasePlayer() | ||||
|     } | ||||
| 
 | ||||
|     override fun onPause() { | ||||
|         Log.i(TAG, "onPause") | ||||
|         saveData() | ||||
|         exoPlayer?.pause() | ||||
|         releasePlayer() | ||||
|         //releasePlayer() | ||||
|     } | ||||
| 
 | ||||
|     override fun onResume(context: Context) { | ||||
|  |  | |||
|  | @ -88,6 +88,7 @@ const val DOUBLE_TAB_PAUSE_PERCENTAGE = 0.15        // in both directions | |||
| open class FullScreenPlayer : AbstractPlayerFragment() { | ||||
|     protected open var lockRotation = true | ||||
|     protected open var isFullScreenPlayer = true | ||||
|     protected open var isTv = false | ||||
| 
 | ||||
|     // state of player UI | ||||
|     protected var isShowing = false | ||||
|  | @ -1055,7 +1056,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | |||
| 
 | ||||
|                     // netflix capture back and hide ~monke | ||||
|                     KeyEvent.KEYCODE_BACK -> { | ||||
|                         if (isShowing) { | ||||
|                         if (isShowing && isTv) { | ||||
|                             onClickChange() | ||||
|                             return true | ||||
|                         } | ||||
|  | @ -1257,6 +1258,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() { | |||
|         player_intro_play?.setOnClickListener { | ||||
|             player_intro_play?.isGone = true | ||||
|             player.handleEvent(CSPlayerEvent.Play) | ||||
|             updateUIVisibility() | ||||
|         } | ||||
| 
 | ||||
|         // it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar | ||||
|  |  | |||
|  | @ -914,8 +914,9 @@ class GeneratorPlayer : FullScreenPlayer() { | |||
|         savedInstanceState: Bundle? | ||||
|     ): View? { | ||||
|         // this is used instead of layout-television to follow the settings and some TV devices are not classified as TV for some reason | ||||
|         isTv = context?.isTvSettings() == true | ||||
|         layout = | ||||
|             if (context?.isTvSettings() == true) R.layout.fragment_player_tv else R.layout.fragment_player | ||||
|             if (isTv) R.layout.fragment_player_tv else R.layout.fragment_player | ||||
| 
 | ||||
|         viewModel = ViewModelProvider(this)[PlayerGeneratorViewModel::class.java] | ||||
|         sync = ViewModelProvider(this)[SyncViewModel::class.java] | ||||
|  |  | |||
|  | @ -41,6 +41,7 @@ import com.google.android.material.button.MaterialButton | |||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiFromName | ||||
| import com.lagradost.cloudstream3.APIHolder.getId | ||||
| import com.lagradost.cloudstream3.APIHolder.unixTime | ||||
| import com.lagradost.cloudstream3.APIHolder.updateHasTrailers | ||||
| import com.lagradost.cloudstream3.AcraApplication.Companion.setKey | ||||
| import com.lagradost.cloudstream3.CommonActivity.getCastSession | ||||
|  | @ -100,6 +101,8 @@ import kotlinx.coroutines.Job | |||
| import kotlinx.coroutines.runBlocking | ||||
| import kotlinx.coroutines.withContext | ||||
| import java.io.File | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| 
 | ||||
| const val MAX_SYNO_LENGH = 1000 | ||||
| 
 | ||||
|  | @ -654,6 +657,55 @@ class ResultFragment : ResultTrailerPlayer() { | |||
|         loadTrailer() | ||||
|     } | ||||
| 
 | ||||
|     private fun setNextEpisode(nextAiring: NextAiring?) { | ||||
|         result_next_airing_holder?.isVisible = | ||||
|             if (nextAiring == null || nextAiring.episode <= 0 || nextAiring.unixTime <= unixTime) { | ||||
|                 false | ||||
|             } else { | ||||
|                 val seconds = nextAiring.unixTime - unixTime | ||||
|                 val days = TimeUnit.SECONDS.toDays(seconds) | ||||
|                 val hours: Long = TimeUnit.SECONDS.toHours(seconds) - days * 24 | ||||
|                 val minute = | ||||
|                     TimeUnit.SECONDS.toMinutes(seconds) - TimeUnit.SECONDS.toHours(seconds) * 60 | ||||
|                 // val second = | ||||
|                 //    TimeUnit.SECONDS.toSeconds(seconds) - TimeUnit.SECONDS.toMinutes(seconds) * 60 | ||||
|                 try { | ||||
|                     val ctx = context | ||||
|                     if (ctx == null) { | ||||
|                         false | ||||
|                     } else { | ||||
|                         when { | ||||
|                             days > 0 -> { | ||||
|                                 ctx.getString(R.string.next_episode_time_day_format).format( | ||||
|                                     days, | ||||
|                                     hours, | ||||
|                                     minute | ||||
|                                 ) | ||||
|                             } | ||||
|                             hours > 0 -> ctx.getString(R.string.next_episode_time_hour_format) | ||||
|                                 .format( | ||||
|                                     hours, | ||||
|                                     minute | ||||
|                                 ) | ||||
|                             minute > 0 -> ctx.getString(R.string.next_episode_time_min_format) | ||||
|                                 .format( | ||||
|                                     minute | ||||
|                                 ) | ||||
|                             else -> null | ||||
|                         }?.also { text -> | ||||
|                             result_next_airing_time?.text = text | ||||
|                             result_next_airing?.text = | ||||
|                                 ctx.getString(R.string.next_episode_format).format(nextAiring.episode) | ||||
|                         } != null | ||||
|                     } | ||||
|                 } catch (e: Exception) { // mistranslation | ||||
|                     result_next_airing_holder?.isVisible = false | ||||
|                     logError(e) | ||||
|                     false | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
| 
 | ||||
|     private fun setActors(actors: List<ActorData>?) { | ||||
|         if (actors.isNullOrEmpty()) { | ||||
|             result_cast_text?.isVisible = false | ||||
|  | @ -1801,7 +1853,7 @@ class ResultFragment : ResultTrailerPlayer() { | |||
|                     setRating(d.rating) | ||||
|                     setRecommendations(d.recommendations, null) | ||||
|                     setActors(d.actors) | ||||
| 
 | ||||
|                     setNextEpisode(if (d is EpisodeResponse) d.nextAiring else null) | ||||
|                     setTrailers(d.trailers) | ||||
| 
 | ||||
|                     if (syncModel.addSyncs(d.syncData)) { | ||||
|  |  | |||
|  | @ -85,7 +85,6 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen | |||
|         isFullScreenPlayer = fullscreen | ||||
|         lockRotation = fullscreen | ||||
|         player_fullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24) | ||||
|         uiReset() | ||||
|         if (fullscreen) { | ||||
|             enterFullscreen() | ||||
|             result_top_bar?.isVisible = false | ||||
|  | @ -106,6 +105,7 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen | |||
|             exitFullscreen() | ||||
|         } | ||||
|         fixPlayerSize() | ||||
|         uiReset() | ||||
|     } | ||||
| 
 | ||||
|     override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||||
|  |  | |||
|  | @ -5,7 +5,6 @@ import androidx.lifecycle.LiveData | |||
| import androidx.lifecycle.MutableLiveData | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import androidx.preference.PreferenceManager | ||||
| import com.lagradost.cloudstream3.* | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings | ||||
| import com.lagradost.cloudstream3.APIHolder.getApiFromNameNull | ||||
|  | @ -29,7 +28,6 @@ import com.lagradost.cloudstream3.ui.WatchType | |||
| import com.lagradost.cloudstream3.ui.player.IGenerator | ||||
| import com.lagradost.cloudstream3.ui.player.RepoLinkGenerator | ||||
| import com.lagradost.cloudstream3.ui.player.SubtitleData | ||||
| import com.lagradost.cloudstream3.ui.search.SearchResultBuilder | ||||
| import com.lagradost.cloudstream3.utils.Coroutines.ioWork | ||||
| import com.lagradost.cloudstream3.utils.DOWNLOAD_HEADER_CACHE | ||||
| import com.lagradost.cloudstream3.utils.DataStoreHelper | ||||
|  | @ -144,6 +142,10 @@ class ResultViewModel : ViewModel() { | |||
|             posterUrl = posterUrl ?: meta.posterUrl ?: meta.backgroundPosterUrl | ||||
|             actors = actors ?: meta.actors | ||||
| 
 | ||||
|             if (this is EpisodeResponse) { | ||||
|                 nextAiring = nextAiring ?: meta.nextAiring | ||||
|             } | ||||
| 
 | ||||
|             for ((k, v) in syncs ?: emptyMap()) { | ||||
|                 syncData[k] = v | ||||
|             } | ||||
|  | @ -162,7 +164,6 @@ class ResultViewModel : ViewModel() { | |||
|             argamap({ | ||||
|                 addTrailer(meta.trailers) | ||||
|             }, { | ||||
| 
 | ||||
|                 if (this !is AnimeLoadResponse) return@argamap | ||||
|                 val map = getEpisodesDetails(getMalId(), getAniListId(), isResponseRequired = false) | ||||
|                 if (map.isNullOrEmpty()) return@argamap | ||||
|  |  | |||
|  | @ -140,6 +140,8 @@ val extractorApis: Array<ExtractorApi> = arrayOf( | |||
|     //mixdrop extractors | ||||
|     MixDropBz(), | ||||
|     MixDropCh(), | ||||
|     MixDropTo(), | ||||
| 
 | ||||
|     MixDrop(), | ||||
| 
 | ||||
|     Mcloud(), | ||||
|  | @ -186,6 +188,7 @@ val extractorApis: Array<ExtractorApi> = arrayOf( | |||
|     DoodLaExtractor(), | ||||
|     DoodWsExtractor(), | ||||
|     DoodShExtractor(), | ||||
|     DoodWatchExtractor(), | ||||
| 
 | ||||
|     AsianLoad(), | ||||
| 
 | ||||
|  |  | |||
|  | @ -139,6 +139,7 @@ | |||
|                     android:orientation="vertical"> | ||||
| 
 | ||||
|                 <com.facebook.shimmer.ShimmerFrameLayout | ||||
|                         tools:visibility="gone" | ||||
|                         android:visibility="gone" | ||||
|                         android:id="@+id/result_trailer_loading" | ||||
|                         android:layout_width="match_parent" | ||||
|  | @ -148,8 +149,7 @@ | |||
|                         app:shimmer_auto_start="true" | ||||
|                         app:shimmer_base_alpha="0.2" | ||||
|                         app:shimmer_duration="@integer/loading_time" | ||||
|                         app:shimmer_highlight_alpha="0.3" | ||||
|                         tools:visibility="visible"> | ||||
|                         app:shimmer_highlight_alpha="0.3"> | ||||
| 
 | ||||
|                     <LinearLayout | ||||
|                             android:layout_width="match_parent" | ||||
|  | @ -423,6 +423,7 @@ | |||
|                             tools:text="Cast: Joe Ligma" /> | ||||
| 
 | ||||
|                     <androidx.recyclerview.widget.RecyclerView | ||||
|                             tools:visibility="gone" | ||||
|                             android:nextFocusUp="@id/result_bookmark_button" | ||||
|                             android:nextFocusDown="@id/result_play_movie" | ||||
| 
 | ||||
|  | @ -669,6 +670,7 @@ | |||
|                                 android:visibility="gone" | ||||
|                                 tools:visibility="visible"> | ||||
| 
 | ||||
| 
 | ||||
|                             <com.google.android.material.button.MaterialButton | ||||
|                                     android:id="@+id/result_resume_series_button" | ||||
|                                     style="@style/WhiteButton" | ||||
|  | @ -747,6 +749,7 @@ | |||
|                                     tools:text="69m\nremaining" /> | ||||
|                         </LinearLayout> | ||||
| 
 | ||||
| 
 | ||||
|                         <LinearLayout | ||||
|                                 android:id="@+id/result_episodes_tab" | ||||
|                                 android:layout_width="match_parent" | ||||
|  | @ -765,6 +768,7 @@ | |||
|                                         style="@style/MultiSelectButton" | ||||
|                                         android:layout_gravity="center_vertical" | ||||
|                                         android:layout_marginStart="0dp" | ||||
|                                         android:layout_marginEnd="10dp" | ||||
|                                         android:nextFocusLeft="@id/result_episode_select" | ||||
|                                         android:nextFocusRight="@id/result_episode_select" | ||||
|                                         android:nextFocusUp="@id/result_description" | ||||
|  | @ -779,6 +783,8 @@ | |||
| 
 | ||||
|                                         android:layout_gravity="center_vertical" | ||||
|                                         android:layout_marginStart="0dp" | ||||
|                                         android:layout_marginEnd="10dp" | ||||
| 
 | ||||
|                                         android:nextFocusLeft="@id/result_season_button" | ||||
|                                         android:nextFocusRight="@id/result_season_button" | ||||
| 
 | ||||
|  | @ -794,6 +800,7 @@ | |||
| 
 | ||||
|                                         android:layout_gravity="center_vertical" | ||||
|                                         android:layout_marginStart="0dp" | ||||
|                                         android:layout_marginEnd="10dp" | ||||
|                                         android:nextFocusLeft="@id/result_season_button" | ||||
|                                         android:nextFocusRight="@id/result_season_button" | ||||
| 
 | ||||
|  | @ -816,6 +823,37 @@ | |||
|                                         tools:text="8 Episodes" /> | ||||
|                             </LinearLayout> | ||||
| 
 | ||||
| 
 | ||||
|                             <!--TODO add next airing--> | ||||
|                             <LinearLayout | ||||
|                                     android:id="@+id/result_next_airing_holder" | ||||
|                                     android:layout_gravity="start" | ||||
|                                     android:paddingBottom="15dp" | ||||
|                                     android:orientation="horizontal" | ||||
|                                     android:layout_width="wrap_content" | ||||
|                                     android:layout_height="wrap_content"> | ||||
|                                 <TextView | ||||
|                                         android:gravity="center" | ||||
| 
 | ||||
|                                         android:id="@+id/result_next_airing" | ||||
|                                         android:layout_width="match_parent" | ||||
|                                         android:layout_height="wrap_content" | ||||
|                                         android:textColor="?attr/grayTextColor" | ||||
|                                         android:textSize="17sp" | ||||
|                                         android:textStyle="normal" | ||||
|                                         android:text="Episode 1022 will be released in" /> | ||||
|                                 <TextView | ||||
|                                         android:paddingStart="5dp" | ||||
|                                         android:gravity="center" | ||||
|                                         android:id="@+id/result_next_airing_time" | ||||
|                                         android:layout_width="match_parent" | ||||
|                                         android:layout_height="wrap_content" | ||||
|                                         android:textColor="?attr/textColor" | ||||
|                                         android:textSize="17sp" | ||||
|                                         android:textStyle="normal" | ||||
|                                         tools:text="5d 3h 30m" /> | ||||
|                             </LinearLayout> | ||||
| 
 | ||||
|                             <com.facebook.shimmer.ShimmerFrameLayout | ||||
|                                     android:id="@+id/result_episode_loading" | ||||
|                                     android:layout_width="match_parent" | ||||
|  |  | |||
|  | @ -67,6 +67,10 @@ | |||
|     <string name="year_format" translatable="false" formatted="true">%d</string> | ||||
|     <string name="app_dub_sub_episode_text_format" formatted="true">%s Ep %d</string> | ||||
|     <string name="cast_format" formatted="true">Cast: %s</string> | ||||
|     <string name="next_episode_format" formatted="true">Episode %d will be released in</string> | ||||
|     <string name="next_episode_time_day_format" formatted="true">%dd %dh %dm</string> | ||||
|     <string name="next_episode_time_hour_format" formatted="true">%dh %dm</string> | ||||
|     <string name="next_episode_time_min_format" formatted="true">%dm</string> | ||||
| 
 | ||||
|     <!-- IS NOT NEEDED TO TRANSLATE AS THEY ARE ONLY USED FOR SCREEN READERS AND WONT SHOW UP TO NORMAL USERS --> | ||||
|     <string name="result_poster_img_des">Poster</string> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue