package com.lagradost.cloudstream3.movieproviders import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.* import com.lagradost.cloudstream3.APIHolder.unixTime import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest import com.lagradost.cloudstream3.utils.getQualityFromName import org.jsoup.Jsoup //BE AWARE THAT weboas.is is a clone of lookmovie class LookMovieProvider : MainAPI() { override val hasQuickSearch: Boolean get() = true override val name: String get() = "LookMovie" override val mainUrl: String get() = "https://lookmovie.io" data class LookMovieSearchResult( @JsonProperty("backdrop") val backdrop: String?, @JsonProperty("imdb_rating") val imdb_rating: String, @JsonProperty("poster") val poster: String?, @JsonProperty("slug") val slug: String, @JsonProperty("title") val title: String, @JsonProperty("year") val year: String?, // @JsonProperty("flag_quality") val flag_quality: Int?, ) data class LookMovieTokenRoot( @JsonProperty("data") val data: LookMovieTokenResult?, @JsonProperty("success") val success: Boolean, ) data class LookMovieTokenResult( @JsonProperty("accessToken") val accessToken: String, @JsonProperty("subtitles") val subtitles: List?, ) data class LookMovieTokenSubtitle( @JsonProperty("language") val language: String, @JsonProperty("source") val source: String?, //@JsonProperty("source_id") val source_id: String, //@JsonProperty("kind") val kind: String, //@JsonProperty("id") val id: String, @JsonProperty("file") val file: String, ) data class LookMovieSearchResultRoot( // @JsonProperty("per_page") val per_page: Int?, // @JsonProperty("total") val total: Int?, @JsonProperty("result") val result: List?, ) data class LookMovieEpisode( @JsonProperty("title") var title: String, @JsonProperty("index") var index: String, @JsonProperty("episode") var episode: String, @JsonProperty("id_episode") var idEpisode: Int, @JsonProperty("season") var season: String, ) override fun quickSearch(query: String): ArrayList { val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query" val movieResponse = khttp.get(movieUrl) val movies = mapper.readValue(movieResponse.text).result val showsUrl = "$mainUrl/api/v1/shows/search/?q=$query" val showsResponse = khttp.get(showsUrl) val shows = mapper.readValue(showsResponse.text).result val returnValue = ArrayList() if (!movies.isNullOrEmpty()) { for (m in movies) { val url = "$mainUrl/movies/view/${m.slug}" returnValue.add( MovieSearchResponse( m.title, url, url,//m.slug, this.name, TvType.Movie, m.poster ?: m.backdrop, m.year?.toIntOrNull() ) ) } } if (!shows.isNullOrEmpty()) { for (s in shows) { val url = "$mainUrl/shows/view/${s.slug}" returnValue.add( MovieSearchResponse( s.title, url, url,//s.slug, this.name, TvType.TvSeries, s.poster ?: s.backdrop, s.year?.toIntOrNull() ) ) } } return returnValue } override fun search(query: String): ArrayList { fun search(query: String, isMovie: Boolean): ArrayList { val url = "$mainUrl/${if (isMovie) "movies" else "shows"}/search/?q=$query" val response = khttp.get(url) val document = Jsoup.parse(response.text) val items = document.select("div.flex-wrap-movielist > div.movie-item-style-1") val returnValue = ArrayList() items.forEach { item -> val titleHolder = item.selectFirst("> div.mv-item-infor > h6 > a") val href = fixUrl(titleHolder.attr("href")) val name = titleHolder.text() val posterHolder = item.selectFirst("> div.image__placeholder > a") val poster = posterHolder.selectFirst("> img")?.attr("data-src") val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull() returnValue.add( if (isMovie) { MovieSearchResponse( name, href, href, this.name, TvType.Movie, poster, year ) } else TvSeriesSearchResponse( name, href, href, this.name, TvType.TvSeries, poster, year, null ) ) } return returnValue } val movieList = search(query, true) val seriesList = search(query, false) movieList.addAll(seriesList) return movieList } data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean) private fun addSubtitles(subs: List?, subtitleCallback: (SubtitleFile) -> Unit) { if (subs == null) return subs.forEach { if (it.file.endsWith(".vtt")) subtitleCallback.invoke(SubtitleFile(it.language, fixUrl(it.file))) } } private fun loadCurrentLinks(url: String, callback: (ExtractorLink) -> Unit) { val response = khttp.get(url.replace("\$unixtime", unixTime.toString())) M3u8Manifest.extractLinks(response.text).forEach { callback.invoke( ExtractorLink( this.name, "${this.name} - ${it.second}", fixUrl(it.first), "", getQualityFromName(it.second), true ) ) } } override fun loadLinks( data: String, isCasting: Boolean, subtitleCallback: (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit ): Boolean { val localData: LookMovieLinkLoad = mapper.readValue(data) if (localData.isMovie) { val tokenResponse = khttp.get(localData.url) val root = mapper.readValue(tokenResponse.text) val accessToken = root.data?.accessToken ?: return false addSubtitles(root.data.subtitles, subtitleCallback) loadCurrentLinks(localData.extraUrl.replace("\$accessToken", accessToken), callback) return true } else { loadCurrentLinks(localData.url, callback) val subResponse = khttp.get(localData.extraUrl) val subs = mapper.readValue>(subResponse.text) addSubtitles(subs, subtitleCallback) } return true } override fun load(slug: String): LoadResponse? { val response = khttp.get(slug) val document = Jsoup.parse(response.text) val isMovie = slug.contains("/movies/") val watchHeader = document.selectFirst("div.watch-heading") val nameHeader = watchHeader.selectFirst("> h1.bd-hd") val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull() val title = nameHeader.ownText() val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text()) val imgElement = document.selectFirst("div.movie-img > p.movie__poster") val img = imgElement?.attr("style") var poster = if (img.isNullOrEmpty()) null else "url\\((.*?)\\)".toRegex().find(img)?.groupValues?.get(1) if (poster.isNullOrEmpty()) poster = imgElement?.attr("data-background-image") val descript = document.selectFirst("p.description-short").text() val id = "${if (isMovie) "id_movie" else "id_show"}:(.*?),".toRegex().find(response.text)?.groupValues?.get(1) ?.replace(" ", "") ?: return null val realSlug = slug.replace("$mainUrl/${if (isMovie) "movies" else "shows"}/view/", "") val realUrl = "$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1" if (isMovie) { val localData = mapper.writeValueAsString( LookMovieLinkLoad( realUrl, "$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8", true ) ) return MovieLoadResponse( title, slug, this.name, TvType.Movie, localData, poster, year, descript, null, rating ) } else { val tokenResponse = khttp.get(realUrl) val root = mapper.readValue(tokenResponse.text) val accessToken = root.data?.accessToken ?: return null val window = "window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1) ?: return null // val id = "id_show:(.*?),".toRegex().find(response.text)?.groupValues?.get(1) ?: return null val season = "seasons:.*\\[((.|\\n)*?)]".toRegex().find(window)?.groupValues?.get(1) ?: return null fun String.fixSeasonJson(replace: String): String { return this.replace("$replace:", "\"$replace\":") } val json = season .replace("\'", "\"") .fixSeasonJson("title") .fixSeasonJson("id_episode") .fixSeasonJson("episode") .fixSeasonJson("index") .fixSeasonJson("season") val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]" val episodes = mapper.readValue>(realJson).map { val localData = mapper.writeValueAsString( LookMovieLinkLoad( "$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8", "https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=${it.idEpisode}", false ) ) TvSeriesEpisode( it.title, it.season.toIntOrNull(), it.episode.toIntOrNull(), localData ) }.toList() return TvSeriesLoadResponse( title, slug, this.name, TvType.TvSeries, ArrayList(episodes), poster, year, descript, null, null, rating ) } } }