cloudstream/app/src/main/java/com/lagradost/cloudstream3/movieproviders/LookMovieProvider.kt

300 lines
12 KiB
Kotlin
Raw Normal View History

2021-06-24 17:45:59 +00:00
package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue
import com.lagradost.cloudstream3.*
2021-06-24 23:20:49 +00:00
import com.lagradost.cloudstream3.APIHolder.unixTime
2021-06-24 17:45:59 +00:00
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.extractors.M3u8Manifest
2021-06-24 23:20:49 +00:00
import com.lagradost.cloudstream3.utils.getQualityFromName
2021-06-24 17:45:59 +00:00
import org.jsoup.Jsoup
2021-06-26 14:44:53 +00:00
//BE AWARE THAT weboas.is is a clone of lookmovie
2021-06-24 17:45:59 +00:00
class LookMovieProvider : MainAPI() {
override val hasQuickSearch: Boolean
get() = true
override val name: String
get() = "LookMovie"
override val mainUrl: String
get() = "https://lookmovie.io"
2021-08-14 17:31:27 +00:00
override val supportedTypes: Set<TvType>
get() = setOf(
TvType.Movie,
TvType.TvSeries,
)
2021-06-24 17:45:59 +00:00
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?,
)
2021-06-24 23:20:49 +00:00
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<LookMovieTokenSubtitle>?,
)
data class LookMovieTokenSubtitle(
@JsonProperty("language") val language: String,
2021-07-01 20:11:33 +00:00
@JsonProperty("source") val source: String?,
2021-06-24 23:20:49 +00:00
//@JsonProperty("source_id") val source_id: String,
//@JsonProperty("kind") val kind: String,
//@JsonProperty("id") val id: String,
@JsonProperty("file") val file: String,
)
2021-06-24 17:45:59 +00:00
data class LookMovieSearchResultRoot(
// @JsonProperty("per_page") val per_page: Int?,
// @JsonProperty("total") val total: Int?,
@JsonProperty("result") val result: List<LookMovieSearchResult>?,
)
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,
)
2021-06-26 14:44:53 +00:00
override fun quickSearch(query: String): ArrayList<SearchResponse> {
2021-06-24 17:45:59 +00:00
val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query"
val movieResponse = khttp.get(movieUrl)
val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse.text).result
val showsUrl = "$mainUrl/api/v1/shows/search/?q=$query"
val showsResponse = khttp.get(showsUrl)
val shows = mapper.readValue<LookMovieSearchResultRoot>(showsResponse.text).result
val returnValue = ArrayList<SearchResponse>()
if (!movies.isNullOrEmpty()) {
for (m in movies) {
val url = "$mainUrl/movies/view/${m.slug}"
2021-07-01 20:11:33 +00:00
returnValue.add(
MovieSearchResponse(
m.title,
url,
this.name,
TvType.Movie,
m.poster ?: m.backdrop,
m.year?.toIntOrNull()
)
)
2021-06-24 17:45:59 +00:00
}
}
if (!shows.isNullOrEmpty()) {
for (s in shows) {
val url = "$mainUrl/shows/view/${s.slug}"
2021-07-01 20:11:33 +00:00
returnValue.add(
MovieSearchResponse(
s.title,
url,
this.name,
TvType.TvSeries,
s.poster ?: s.backdrop,
s.year?.toIntOrNull()
)
)
2021-06-24 17:45:59 +00:00
}
}
return returnValue
}
override fun search(query: String): ArrayList<SearchResponse> {
fun search(query: String, isMovie: Boolean): ArrayList<SearchResponse> {
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<SearchResponse>()
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()
2021-07-01 20:11:33 +00:00
returnValue.add(
if (isMovie) {
MovieSearchResponse(
2021-07-30 14:09:57 +00:00
name, href, this.name, TvType.Movie, poster, year
2021-07-01 20:11:33 +00:00
)
} else
TvSeriesSearchResponse(
2021-07-30 14:09:57 +00:00
name, href, this.name, TvType.TvSeries, poster, year, null
2021-07-01 20:11:33 +00:00
)
2021-06-24 17:45:59 +00:00
)
}
return returnValue
}
val movieList = search(query, true)
val seriesList = search(query, false)
movieList.addAll(seriesList)
return movieList
}
2021-07-01 20:11:33 +00:00
data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean)
private fun addSubtitles(subs: List<LookMovieTokenSubtitle>?, subtitleCallback: (SubtitleFile) -> Unit) {
if (subs == null) return
subs.forEach {
2021-07-02 18:46:18 +00:00
if (it.file.endsWith(".vtt"))
2021-07-01 20:11:33 +00:00
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()))
2021-06-26 14:44:53 +00:00
M3u8Manifest.extractLinks(response.text).forEach {
2021-07-01 20:11:33 +00:00
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<LookMovieTokenRoot>(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<List<LookMovieTokenSubtitle>>(subResponse.text)
addSubtitles(subs, subtitleCallback)
2021-06-24 23:20:49 +00:00
}
return true
2021-06-24 17:45:59 +00:00
}
2021-07-30 14:09:57 +00:00
override fun load(url: String): LoadResponse? {
val response = khttp.get(url)
2021-06-24 17:45:59 +00:00
val document = Jsoup.parse(response.text)
2021-07-30 14:09:57 +00:00
val isMovie = url.contains("/movies/")
2021-06-24 17:45:59 +00:00
val watchHeader = document.selectFirst("div.watch-heading")
val nameHeader = watchHeader.selectFirst("> h1.bd-hd")
val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull()
2021-07-01 20:11:33 +00:00
val title = nameHeader.ownText()
2021-06-24 17:45:59 +00:00
val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text())
2021-06-24 23:20:49 +00:00
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")
2021-06-24 17:45:59 +00:00
val descript = document.selectFirst("p.description-short").text()
2021-06-24 23:20:49 +00:00
val id = "${if (isMovie) "id_movie" else "id_show"}:(.*?),".toRegex().find(response.text)?.groupValues?.get(1)
?.replace(" ", "")
2021-06-24 17:45:59 +00:00
?: return null
2021-07-30 14:09:57 +00:00
val realSlug = url.replace("$mainUrl/${if (isMovie) "movies" else "shows"}/view/", "")
2021-06-24 17:45:59 +00:00
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"
2021-06-24 23:20:49 +00:00
2021-06-24 17:45:59 +00:00
if (isMovie) {
2021-07-01 20:11:33 +00:00
val localData = mapper.writeValueAsString(
LookMovieLinkLoad(
realUrl,
"$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8",
true
)
)
return MovieLoadResponse(
title,
2021-07-30 14:09:57 +00:00
url,
2021-06-24 23:20:49 +00:00
this.name,
TvType.Movie,
2021-07-01 20:11:33 +00:00
localData,
2021-06-24 23:20:49 +00:00
poster,
year,
descript,
null,
2021-07-01 20:11:33 +00:00
rating
)
2021-06-24 17:45:59 +00:00
} else {
2021-07-01 20:11:33 +00:00
val tokenResponse = khttp.get(realUrl)
val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text)
val accessToken = root.data?.accessToken ?: return null
2021-06-24 17:45:59 +00:00
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\":")
}
2021-06-24 23:20:49 +00:00
val json = season
2021-06-24 17:45:59 +00:00
.replace("\'", "\"")
.fixSeasonJson("title")
.fixSeasonJson("id_episode")
.fixSeasonJson("episode")
.fixSeasonJson("index")
.fixSeasonJson("season")
2021-06-24 23:20:49 +00:00
val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]"
2021-06-24 17:45:59 +00:00
val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map {
2021-07-01 20:11:33 +00:00
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,
2021-06-24 23:20:49 +00:00
it.season.toIntOrNull(),
it.episode.toIntOrNull(),
2021-07-01 20:11:33 +00:00
localData
)
2021-06-24 17:45:59 +00:00
}.toList()
2021-07-01 20:11:33 +00:00
return TvSeriesLoadResponse(
title,
2021-07-30 14:09:57 +00:00
url,
2021-06-24 17:45:59 +00:00
this.name,
TvType.TvSeries,
ArrayList(episodes),
poster,
year,
descript,
null,
null,
2021-07-01 20:11:33 +00:00
rating
)
2021-06-24 17:45:59 +00:00
}
}
}