forked from recloudstream/cloudstream
small subtitle support
This commit is contained in:
parent
672dc8c8d1
commit
9fc732c68c
12 changed files with 266 additions and 124 deletions
|
@ -72,7 +72,7 @@ abstract class MainAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// callback is fired once a link is found, will return true if method is executed successfully
|
// callback is fired once a link is found, will return true if method is executed successfully
|
||||||
open fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
open fun loadLinks(data: String, isCasting: Boolean, subtitleCallback : (SubtitleFile) -> Unit, callback: (ExtractorLink) -> Unit): Boolean {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,8 @@ enum class TvType {
|
||||||
ONA,
|
ONA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SubtitleFile(val lang : String, val url : String)
|
||||||
|
|
||||||
interface SearchResponse {
|
interface SearchResponse {
|
||||||
val name: String
|
val name: String
|
||||||
val url: String // PUBLIC URL FOR OPEN IN APP
|
val url: String // PUBLIC URL FOR OPEN IN APP
|
||||||
|
|
|
@ -86,7 +86,8 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
title, href, getSlug(href), this.name, TvType.Movie, img, null
|
title, href, getSlug(href), this.name, TvType.Movie, img, null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AnimeSearchResponse(title,
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
href,
|
href,
|
||||||
getSlug(href),
|
getSlug(href),
|
||||||
this.name,
|
this.name,
|
||||||
|
@ -96,8 +97,10 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
null,
|
null,
|
||||||
EnumSet.of(DubStatus.Dubbed),
|
EnumSet.of(DubStatus.Dubbed),
|
||||||
null,
|
null,
|
||||||
null)
|
null
|
||||||
})
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
@ -121,7 +124,8 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
title, href, getSlug(href), this.name, TvType.Movie, img, null
|
title, href, getSlug(href), this.name, TvType.Movie, img, null
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
AnimeSearchResponse(title,
|
AnimeSearchResponse(
|
||||||
|
title,
|
||||||
href,
|
href,
|
||||||
getSlug(href),
|
getSlug(href),
|
||||||
this.name,
|
this.name,
|
||||||
|
@ -131,14 +135,21 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
null,
|
null,
|
||||||
EnumSet.of(DubStatus.Dubbed),
|
EnumSet.of(DubStatus.Dubbed),
|
||||||
null,
|
null,
|
||||||
null)
|
null
|
||||||
})
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE
|
val serversHTML = (if (data.startsWith(mainUrl)) { // CLASSIC EPISODE
|
||||||
val slug = getSlug(data)
|
val slug = getSlug(data)
|
||||||
getAnimeEpisode(slug, false).serversHTML
|
getAnimeEpisode(slug, false).serversHTML
|
||||||
|
@ -154,11 +165,15 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(txt)
|
val find = "src=\"(.*?)\".*?label=\"(.*?)\"".toRegex().find(txt)
|
||||||
if (find != null) {
|
if (find != null) {
|
||||||
val quality = find.groupValues[2]
|
val quality = find.groupValues[2]
|
||||||
callback.invoke(ExtractorLink(this.name,
|
callback.invoke(
|
||||||
this.name + " " + quality + if (quality.endsWith('p')) "" else 'p',
|
ExtractorLink(
|
||||||
fixUrl(find.groupValues[1]),
|
this.name,
|
||||||
this.mainUrl,
|
this.name + " " + quality + if (quality.endsWith('p')) "" else 'p',
|
||||||
getQualityFromName(quality)))
|
fixUrl(find.groupValues[1]),
|
||||||
|
this.mainUrl,
|
||||||
|
getQualityFromName(quality)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
//IDK
|
//IDK
|
||||||
|
@ -172,7 +187,8 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
val realSlug = slug.replace("movies/", "")
|
val realSlug = slug.replace("movies/", "")
|
||||||
val episode = getAnimeEpisode(realSlug, true)
|
val episode = getAnimeEpisode(realSlug, true)
|
||||||
val poster = episode.previewImg ?: episode.wideImg
|
val poster = episode.previewImg ?: episode.wideImg
|
||||||
return MovieLoadResponse(episode.title,
|
return MovieLoadResponse(
|
||||||
|
episode.title,
|
||||||
realSlug,
|
realSlug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
|
@ -180,7 +196,8 @@ class DubbedAnimeProvider : MainAPI() {
|
||||||
if (poster == null) null else fixUrl(poster),
|
if (poster == null) null else fixUrl(poster),
|
||||||
episode.year?.toIntOrNull(),
|
episode.year?.toIntOrNull(),
|
||||||
episode.desc,
|
episode.desc,
|
||||||
null)
|
null
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val response = khttp.get("$mainUrl/$slug")
|
val response = khttp.get("$mainUrl/$slug")
|
||||||
val document = Jsoup.parse(response.text)
|
val document = Jsoup.parse(response.text)
|
||||||
|
|
|
@ -167,12 +167,14 @@ class ShiroProvider : MainAPI() {
|
||||||
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
override fun quickSearch(query: String): ArrayList<SearchResponse> {
|
||||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||||
|
|
||||||
val response = khttp.get("https://tapi.shiro.is/anime/auto-complete/${
|
val response = khttp.get(
|
||||||
URLEncoder.encode(
|
"https://tapi.shiro.is/anime/auto-complete/${
|
||||||
query,
|
URLEncoder.encode(
|
||||||
"UTF-8"
|
query,
|
||||||
)
|
"UTF-8"
|
||||||
}?token=$token".replace("+", "%20"))
|
)
|
||||||
|
}?token=$token".replace("+", "%20")
|
||||||
|
)
|
||||||
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||||
|
|
||||||
val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) }
|
val mapped = response.let { mapper.readValue<ShiroSearchResponse>(it.text) }
|
||||||
|
@ -185,12 +187,14 @@ class ShiroProvider : MainAPI() {
|
||||||
override fun search(query: String): ArrayList<SearchResponse>? {
|
override fun search(query: String): ArrayList<SearchResponse>? {
|
||||||
if (!autoLoadToken()) return null
|
if (!autoLoadToken()) return null
|
||||||
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
val returnValue: ArrayList<SearchResponse> = ArrayList()
|
||||||
val response = khttp.get("https://tapi.shiro.is/advanced?search=${
|
val response = khttp.get(
|
||||||
URLEncoder.encode(
|
"https://tapi.shiro.is/advanced?search=${
|
||||||
query,
|
URLEncoder.encode(
|
||||||
"UTF-8"
|
query,
|
||||||
)
|
"UTF-8"
|
||||||
}&token=$token".replace("+", "%20"))
|
)
|
||||||
|
}&token=$token".replace("+", "%20")
|
||||||
|
)
|
||||||
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
if (response.text == "{\"status\":\"Found\",\"data\":[]}") return returnValue // OR ELSE WILL CAUSE WEIRD ERROR
|
||||||
|
|
||||||
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
val mapped = response.let { mapper.readValue<ShiroFullSearchResponse>(it.text) }
|
||||||
|
@ -235,7 +239,12 @@ class ShiroProvider : MainAPI() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
return Vidstream().getUrl(data, isCasting) {
|
return Vidstream().getUrl(data, isCasting) {
|
||||||
callback.invoke(it)
|
callback.invoke(it)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,17 +29,26 @@ class HDMProvider : MainAPI() {
|
||||||
return returnValue
|
return returnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
if (data == "") return false
|
if (data == "") return false
|
||||||
val slug = ".*/(.*?)\\.mp4".toRegex().find(data)?.groupValues?.get(1) ?: return false
|
val slug = ".*/(.*?)\\.mp4".toRegex().find(data)?.groupValues?.get(1) ?: return false
|
||||||
val response = khttp.get(data)
|
val response = khttp.get(data)
|
||||||
val key = "playlist\\.m3u8(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return false
|
val key = "playlist\\.m3u8(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return false
|
||||||
callback.invoke(ExtractorLink(this.name,
|
callback.invoke(
|
||||||
this.name,
|
ExtractorLink(
|
||||||
"https://hls.1o.to/vod/$slug/playlist.m3u8$key",
|
this.name,
|
||||||
"",
|
this.name,
|
||||||
Qualities.HD.value,
|
"https://hls.1o.to/vod/$slug/playlist.m3u8$key",
|
||||||
true))
|
"",
|
||||||
|
Qualities.HD.value,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,9 +60,11 @@ class HDMProvider : MainAPI() {
|
||||||
val descript = document.selectFirst("div.synopsis > p").text()
|
val descript = document.selectFirst("div.synopsis > p").text()
|
||||||
val year = document.select("div.movieInfoAll > div.row > div.col-md-6")?.get(1)?.selectFirst("> p > a")?.text()
|
val year = document.select("div.movieInfoAll > div.row > div.col-md-6")?.get(1)?.selectFirst("> p > a")?.text()
|
||||||
?.toIntOrNull()
|
?.toIntOrNull()
|
||||||
val data = "src/player/\\?v=(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return null
|
val data = "src/player/\\?v=(.*?)\"".toRegex().find(response.text)?.groupValues?.get(1) ?: return null
|
||||||
|
|
||||||
return MovieLoadResponse(title, slug, this.name, TvType.Movie,
|
return MovieLoadResponse(
|
||||||
"$mainUrl/src/player/?v=$data", poster, year, descript, null)
|
title, slug, this.name, TvType.Movie,
|
||||||
|
"$mainUrl/src/player/?v=$data", poster, year, descript, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,7 +5,6 @@ import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTime
|
import com.lagradost.cloudstream3.APIHolder.unixTime
|
||||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.Qualities
|
|
||||||
import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest
|
import com.lagradost.cloudstream3.utils.extractors.M3u8Manifest
|
||||||
import com.lagradost.cloudstream3.utils.getQualityFromName
|
import com.lagradost.cloudstream3.utils.getQualityFromName
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
|
@ -43,7 +42,7 @@ class LookMovieProvider : MainAPI() {
|
||||||
|
|
||||||
data class LookMovieTokenSubtitle(
|
data class LookMovieTokenSubtitle(
|
||||||
@JsonProperty("language") val language: String,
|
@JsonProperty("language") val language: String,
|
||||||
//@JsonProperty("source") val source: String,
|
@JsonProperty("source") val source: String?,
|
||||||
//@JsonProperty("source_id") val source_id: String,
|
//@JsonProperty("source_id") val source_id: String,
|
||||||
//@JsonProperty("kind") val kind: String,
|
//@JsonProperty("kind") val kind: String,
|
||||||
//@JsonProperty("id") val id: String,
|
//@JsonProperty("id") val id: String,
|
||||||
|
@ -77,26 +76,34 @@ class LookMovieProvider : MainAPI() {
|
||||||
if (!movies.isNullOrEmpty()) {
|
if (!movies.isNullOrEmpty()) {
|
||||||
for (m in movies) {
|
for (m in movies) {
|
||||||
val url = "$mainUrl/movies/view/${m.slug}"
|
val url = "$mainUrl/movies/view/${m.slug}"
|
||||||
returnValue.add(MovieSearchResponse(m.title,
|
returnValue.add(
|
||||||
url,
|
MovieSearchResponse(
|
||||||
url,//m.slug,
|
m.title,
|
||||||
this.name,
|
url,
|
||||||
TvType.Movie,
|
url,//m.slug,
|
||||||
m.poster ?: m.backdrop,
|
this.name,
|
||||||
m.year?.toIntOrNull()))
|
TvType.Movie,
|
||||||
|
m.poster ?: m.backdrop,
|
||||||
|
m.year?.toIntOrNull()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shows.isNullOrEmpty()) {
|
if (!shows.isNullOrEmpty()) {
|
||||||
for (s in shows) {
|
for (s in shows) {
|
||||||
val url = "$mainUrl/shows/view/${s.slug}"
|
val url = "$mainUrl/shows/view/${s.slug}"
|
||||||
returnValue.add(MovieSearchResponse(s.title,
|
returnValue.add(
|
||||||
url,
|
MovieSearchResponse(
|
||||||
url,//s.slug,
|
s.title,
|
||||||
this.name,
|
url,
|
||||||
TvType.TvSeries,
|
url,//s.slug,
|
||||||
s.poster ?: s.backdrop,
|
this.name,
|
||||||
s.year?.toIntOrNull()))
|
TvType.TvSeries,
|
||||||
|
s.poster ?: s.backdrop,
|
||||||
|
s.year?.toIntOrNull()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,14 +126,15 @@ class LookMovieProvider : MainAPI() {
|
||||||
val poster = posterHolder.selectFirst("> img")?.attr("data-src")
|
val poster = posterHolder.selectFirst("> img")?.attr("data-src")
|
||||||
val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull()
|
val year = posterHolder.selectFirst("> p.year")?.text()?.toIntOrNull()
|
||||||
|
|
||||||
returnValue.add(if (isMovie) {
|
returnValue.add(
|
||||||
MovieSearchResponse(
|
if (isMovie) {
|
||||||
name, href, href, this.name, TvType.Movie, poster, year
|
MovieSearchResponse(
|
||||||
)
|
name, href, href, this.name, TvType.Movie, poster, year
|
||||||
} else
|
)
|
||||||
TvSeriesSearchResponse(
|
} else
|
||||||
name, href, href, this.name, TvType.TvSeries, poster, year, null
|
TvSeriesSearchResponse(
|
||||||
)
|
name, href, href, this.name, TvType.TvSeries, poster, year, null
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return returnValue
|
return returnValue
|
||||||
|
@ -138,15 +146,52 @@ class LookMovieProvider : MainAPI() {
|
||||||
return movieList
|
return movieList
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
data class LookMovieLinkLoad(val url: String, val extraUrl: String, val isMovie: Boolean)
|
||||||
val response = khttp.get(data.replace("\$unixtime", unixTime.toString()))
|
|
||||||
|
private fun addSubtitles(subs: List<LookMovieTokenSubtitle>?, subtitleCallback: (SubtitleFile) -> Unit) {
|
||||||
|
if (subs == null) return
|
||||||
|
subs.forEach {
|
||||||
|
if (it.source != "opensubtitle")
|
||||||
|
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 {
|
M3u8Manifest.extractLinks(response.text).forEach {
|
||||||
callback.invoke(ExtractorLink(this.name,
|
callback.invoke(
|
||||||
"${this.name} - ${it.second}",
|
ExtractorLink(
|
||||||
fixUrl(it.first),
|
this.name,
|
||||||
"",
|
"${this.name} - ${it.second}",
|
||||||
getQualityFromName(it.second),
|
fixUrl(it.first),
|
||||||
true))
|
"",
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -159,7 +204,7 @@ class LookMovieProvider : MainAPI() {
|
||||||
val watchHeader = document.selectFirst("div.watch-heading")
|
val watchHeader = document.selectFirst("div.watch-heading")
|
||||||
val nameHeader = watchHeader.selectFirst("> h1.bd-hd")
|
val nameHeader = watchHeader.selectFirst("> h1.bd-hd")
|
||||||
val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull()
|
val year = nameHeader.selectFirst("> span")?.text()?.toIntOrNull()
|
||||||
val name = nameHeader.ownText()
|
val title = nameHeader.ownText()
|
||||||
val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text())
|
val rating = parseRating(watchHeader.selectFirst("> div.movie-rate > div.rate > p > span").text())
|
||||||
val imgElement = document.selectFirst("div.movie-img > p.movie__poster")
|
val imgElement = document.selectFirst("div.movie-img > p.movie__poster")
|
||||||
val img = imgElement?.attr("style")
|
val img = imgElement?.attr("style")
|
||||||
|
@ -173,22 +218,31 @@ class LookMovieProvider : MainAPI() {
|
||||||
val realUrl =
|
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"
|
"$mainUrl/api/v1/security/${if (isMovie) "movie" else "show"}-access?${if (isMovie) "id_movie=$id" else "slug=$realSlug"}&token=1&sk=&step=1"
|
||||||
|
|
||||||
val tokenResponse = khttp.get(realUrl)
|
|
||||||
val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text)
|
|
||||||
val accessToken = root.data?.accessToken ?: return null
|
|
||||||
|
|
||||||
if (isMovie) {
|
if (isMovie) {
|
||||||
return MovieLoadResponse(name,
|
val localData = mapper.writeValueAsString(
|
||||||
|
LookMovieLinkLoad(
|
||||||
|
realUrl,
|
||||||
|
"$mainUrl/manifests/movies/json/$id/\$unixtime/\$accessToken/master.m3u8",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return MovieLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
"$mainUrl/manifests/movies/json/$id/\$unixtime/$accessToken/master.m3u8",
|
localData,
|
||||||
poster,
|
poster,
|
||||||
year,
|
year,
|
||||||
descript,
|
descript,
|
||||||
null,
|
null,
|
||||||
rating)
|
rating
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
|
val tokenResponse = khttp.get(realUrl)
|
||||||
|
val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text)
|
||||||
|
val accessToken = root.data?.accessToken ?: return null
|
||||||
|
|
||||||
val window =
|
val window =
|
||||||
"window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1)
|
"window\\[\\'show_storage\\'\\] =((.|\\n)*?\\<)".toRegex().find(response.text)?.groupValues?.get(1)
|
||||||
?: return null
|
?: return null
|
||||||
|
@ -208,13 +262,24 @@ class LookMovieProvider : MainAPI() {
|
||||||
val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]"
|
val realJson = "[" + json.substring(0, json.lastIndexOf(',')) + "]"
|
||||||
|
|
||||||
val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map {
|
val episodes = mapper.readValue<List<LookMovieEpisode>>(realJson).map {
|
||||||
TvSeriesEpisode(it.title,
|
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.season.toIntOrNull(),
|
||||||
it.episode.toIntOrNull(),
|
it.episode.toIntOrNull(),
|
||||||
"$mainUrl/manifests/shows/json/$accessToken/\$unixtime/${it.idEpisode}/master.m3u8")
|
localData
|
||||||
|
)
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
return TvSeriesLoadResponse(name,
|
return TvSeriesLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.TvSeries,
|
TvType.TvSeries,
|
||||||
|
@ -224,7 +289,8 @@ class LookMovieProvider : MainAPI() {
|
||||||
descript,
|
descript,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
rating)
|
rating
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -44,22 +44,30 @@ class MeloMovieProvider : MainAPI() {
|
||||||
val currentUrl = "$mainUrl/movie/${i.id}"
|
val currentUrl = "$mainUrl/movie/${i.id}"
|
||||||
val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg"
|
val currentPoster = "$mainUrl/assets/images/poster/${i.imdbId}.jpg"
|
||||||
if (i.type == 2) { // TV-SERIES
|
if (i.type == 2) { // TV-SERIES
|
||||||
returnValue.add(TvSeriesSearchResponse(i.title,
|
returnValue.add(
|
||||||
currentUrl,
|
TvSeriesSearchResponse(
|
||||||
currentUrl,
|
i.title,
|
||||||
this.name,
|
currentUrl,
|
||||||
TvType.TvSeries,
|
currentUrl,
|
||||||
currentPoster,
|
this.name,
|
||||||
i.year,
|
TvType.TvSeries,
|
||||||
null))
|
currentPoster,
|
||||||
|
i.year,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
)
|
||||||
} else if (i.type == 1) { // MOVIE
|
} else if (i.type == 1) { // MOVIE
|
||||||
returnValue.add(MovieSearchResponse(i.title,
|
returnValue.add(
|
||||||
currentUrl,
|
MovieSearchResponse(
|
||||||
currentUrl,
|
i.title,
|
||||||
this.name,
|
currentUrl,
|
||||||
TvType.Movie,
|
currentUrl,
|
||||||
currentUrl,
|
this.name,
|
||||||
i.year))
|
TvType.Movie,
|
||||||
|
currentUrl,
|
||||||
|
i.year
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnValue
|
return returnValue
|
||||||
|
@ -67,7 +75,7 @@ class MeloMovieProvider : MainAPI() {
|
||||||
|
|
||||||
// http not https, the links are not https!
|
// http not https, the links are not https!
|
||||||
private fun fixUrl(url: String): String {
|
private fun fixUrl(url: String): String {
|
||||||
if(url.isEmpty()) return ""
|
if (url.isEmpty()) return ""
|
||||||
|
|
||||||
if (url.startsWith("//")) {
|
if (url.startsWith("//")) {
|
||||||
return "http:$url"
|
return "http:$url"
|
||||||
|
@ -93,7 +101,12 @@ class MeloMovieProvider : MainAPI() {
|
||||||
return mapper.writeValueAsString(parsed)
|
return mapper.writeValueAsString(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
val links = mapper.readValue<List<MeloMovieLink>>(data)
|
val links = mapper.readValue<List<MeloMovieLink>>(data)
|
||||||
for (link in links) {
|
for (link in links) {
|
||||||
callback.invoke(ExtractorLink(this.name, link.name, link.link, "", getQualityFromName(link.name), false))
|
callback.invoke(ExtractorLink(this.name, link.name, link.link, "", getQualityFromName(link.name), false))
|
||||||
|
@ -120,7 +133,8 @@ class MeloMovieProvider : MainAPI() {
|
||||||
|
|
||||||
if (type == 1) { // MOVIE
|
if (type == 1) { // MOVIE
|
||||||
val serialize = document.selectFirst("table.accordion__list")
|
val serialize = document.selectFirst("table.accordion__list")
|
||||||
return MovieLoadResponse(title,
|
return MovieLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
|
@ -128,7 +142,8 @@ class MeloMovieProvider : MainAPI() {
|
||||||
poster,
|
poster,
|
||||||
year,
|
year,
|
||||||
plot,
|
plot,
|
||||||
imdbUrl)
|
imdbUrl
|
||||||
|
)
|
||||||
} else if (type == 2) {
|
} else if (type == 2) {
|
||||||
val episodes = ArrayList<TvSeriesEpisode>()
|
val episodes = ArrayList<TvSeriesEpisode>()
|
||||||
val seasons = document.select("div.accordion__card")
|
val seasons = document.select("div.accordion__card")
|
||||||
|
@ -145,7 +160,8 @@ class MeloMovieProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
episodes.reverse()
|
episodes.reverse()
|
||||||
return TvSeriesLoadResponse(title,
|
return TvSeriesLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.TvSeries,
|
TvType.TvSeries,
|
||||||
|
@ -154,7 +170,8 @@ class MeloMovieProvider : MainAPI() {
|
||||||
year,
|
year,
|
||||||
plot,
|
plot,
|
||||||
null,
|
null,
|
||||||
imdbUrl)
|
imdbUrl
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,12 @@ class TrailersToProvider : MainAPI() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
|
override fun loadLinks(
|
||||||
|
data: String,
|
||||||
|
isCasting: Boolean,
|
||||||
|
subtitleCallback: (SubtitleFile) -> Unit,
|
||||||
|
callback: (ExtractorLink) -> Unit
|
||||||
|
): Boolean {
|
||||||
if (isCasting) return false
|
if (isCasting) return false
|
||||||
val isMovie = data.contains("/web-sources/")
|
val isMovie = data.contains("/web-sources/")
|
||||||
if (isMovie) {
|
if (isMovie) {
|
||||||
|
@ -142,7 +147,8 @@ class TrailersToProvider : MainAPI() {
|
||||||
val epDescript = main.selectFirst("> p")?.text()
|
val epDescript = main.selectFirst("> p")?.text()
|
||||||
TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript)
|
TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript)
|
||||||
}
|
}
|
||||||
return TvSeriesLoadResponse(title,
|
return TvSeriesLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.TvSeries,
|
TvType.TvSeries,
|
||||||
|
@ -155,10 +161,12 @@ class TrailersToProvider : MainAPI() {
|
||||||
rating,
|
rating,
|
||||||
tags,
|
tags,
|
||||||
duration,
|
duration,
|
||||||
trailer)
|
trailer
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null)
|
val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null)
|
||||||
return MovieLoadResponse(title,
|
return MovieLoadResponse(
|
||||||
|
title,
|
||||||
slug,
|
slug,
|
||||||
this.name,
|
this.name,
|
||||||
TvType.Movie,
|
TvType.Movie,
|
||||||
|
@ -170,7 +178,8 @@ class TrailersToProvider : MainAPI() {
|
||||||
rating,
|
rating,
|
||||||
tags,
|
tags,
|
||||||
duration,
|
duration,
|
||||||
trailer)
|
trailer
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -128,7 +128,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
epData,
|
epData,
|
||||||
holder,
|
holder,
|
||||||
index,
|
index,
|
||||||
remoteMediaClient?.mediaInfo?.customData)
|
remoteMediaClient?.mediaInfo?.customData
|
||||||
|
)
|
||||||
|
|
||||||
val startAt = remoteMediaClient?.approximateStreamPosition ?: 0
|
val startAt = remoteMediaClient?.approximateStreamPosition ?: 0
|
||||||
|
|
||||||
|
@ -139,12 +140,17 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
val nextId = remoteMediaClient.mediaQueue.itemIds?.get(currentIdIndex?.plus(1) ?: 0)
|
||||||
|
|
||||||
if (currentIdIndex == null && nextId != null) {
|
if (currentIdIndex == null && nextId != null) {
|
||||||
awaitLinks(remoteMediaClient?.queueInsertAndPlayItem(MediaQueueItem.Builder(
|
awaitLinks(
|
||||||
mediaItem)
|
remoteMediaClient?.queueInsertAndPlayItem(
|
||||||
.build(),
|
MediaQueueItem.Builder(
|
||||||
nextId,
|
mediaItem
|
||||||
startAt,
|
)
|
||||||
JSONObject())) {
|
.build(),
|
||||||
|
nextId,
|
||||||
|
startAt,
|
||||||
|
JSONObject()
|
||||||
|
)
|
||||||
|
) {
|
||||||
loadMirror(index + 1)
|
loadMirror(index + 1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -204,7 +210,7 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
val links = ArrayList<ExtractorLink>()
|
val links = ArrayList<ExtractorLink>()
|
||||||
|
|
||||||
val res = safeApiCall {
|
val res = safeApiCall {
|
||||||
getApiFromName(meta.apiName).loadLinks(epData.data, true) {
|
getApiFromName(meta.apiName).loadLinks(epData.data, true, { subtitleFile -> }) {
|
||||||
for (i in links) {
|
for (i in links) {
|
||||||
if (i.url == it.url) return@loadLinks
|
if (i.url == it.url) return@loadLinks
|
||||||
}
|
}
|
||||||
|
@ -225,7 +231,8 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
epData,
|
epData,
|
||||||
jsonCopy,
|
jsonCopy,
|
||||||
0,
|
0,
|
||||||
done)
|
done
|
||||||
|
)
|
||||||
|
|
||||||
/*fun loadIndex(index: Int) {
|
/*fun loadIndex(index: Int) {
|
||||||
println("LOAD INDEX::::: $index")
|
println("LOAD INDEX::::: $index")
|
||||||
|
@ -240,8 +247,12 @@ class SelectSourceController(val view: ImageView, val activity: ControllerActivi
|
||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
awaitLinks(remoteMediaClient?.queueAppendItem(MediaQueueItem.Builder(mediaInfo).build(),
|
awaitLinks(
|
||||||
JSONObject())) {
|
remoteMediaClient?.queueAppendItem(
|
||||||
|
MediaQueueItem.Builder(mediaInfo).build(),
|
||||||
|
JSONObject()
|
||||||
|
)
|
||||||
|
) {
|
||||||
println("FAILED TO LOAD NEXT ITEM")
|
println("FAILED TO LOAD NEXT ITEM")
|
||||||
// loadIndex(1)
|
// loadIndex(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,11 +147,11 @@ class EpisodeAdapter(
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_CHROME_CAST_EPISODE, card))
|
||||||
} else {
|
} else {
|
||||||
// clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
// clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
// clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||||
clickCallback.invoke(EpisodeClickEvent(ACTION_DOWNLOAD_EPISODE, card))
|
clickCallback.invoke(EpisodeClickEvent(ACTION_PLAY_EPISODE_IN_PLAYER, card))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -206,7 +206,7 @@ class ResultViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
val links = ArrayList<ExtractorLink>()
|
val links = ArrayList<ExtractorLink>()
|
||||||
val localData = safeApiCall {
|
val localData = safeApiCall {
|
||||||
getApiFromName(_apiName.value).loadLinks(data, isCasting) {
|
getApiFromName(_apiName.value).loadLinks(data, isCasting, { subtitleFile -> }) {
|
||||||
for (i in links) {
|
for (i in links) {
|
||||||
if (i.url == it.url) return@loadLinks
|
if (i.url == it.url) return@loadLinks
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@ class SearchFragment : Fragment() {
|
||||||
allApi.providersActive = requireActivity().getApiSettings()
|
allApi.providersActive = requireActivity().getApiSettings()
|
||||||
|
|
||||||
//searchViewModel.search("iron man")
|
//searchViewModel.search("iron man")
|
||||||
(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
//(activity as AppCompatActivity).loadResult("https://shiro.is/overlord-dubbed", "overlord-dubbed", "Shiro")
|
||||||
/*
|
/*
|
||||||
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
|
(requireActivity() as AppCompatActivity).supportFragmentManager.beginTransaction()
|
||||||
.setCustomAnimations(R.anim.enter_anim,
|
.setCustomAnimations(R.anim.enter_anim,
|
||||||
|
|
|
@ -7,7 +7,7 @@ object M3u8Manifest {
|
||||||
val data: ArrayList<Pair<String, String>> = ArrayList()
|
val data: ArrayList<Pair<String, String>> = ArrayList()
|
||||||
"\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach {
|
"\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach {
|
||||||
var quality = it.groupValues[1].replace("auto", "Auto")
|
var quality = it.groupValues[1].replace("auto", "Auto")
|
||||||
if (quality != "Auto") quality += "p"
|
if (quality != "Auto" && !quality.endsWith('p')) quality += "p"
|
||||||
val url = it.groupValues[2]
|
val url = it.groupValues[2]
|
||||||
data.add(Pair(url, quality))
|
data.add(Pair(url, quality))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue