TrailersToProvider

This commit is contained in:
LagradOst 2021-06-26 16:44:53 +02:00
parent cea1839b5c
commit 4cfb7c38c0
6 changed files with 255 additions and 46 deletions

View file

@ -10,6 +10,7 @@ import com.lagradost.cloudstream3.animeproviders.ShiroProvider
import com.lagradost.cloudstream3.movieproviders.HDMProvider
import com.lagradost.cloudstream3.movieproviders.LookMovieProvider
import com.lagradost.cloudstream3.movieproviders.MeloMovieProvider
import com.lagradost.cloudstream3.movieproviders.TrailersToProvider
import com.lagradost.cloudstream3.utils.ExtractorLink
import java.util.*
import kotlin.collections.ArrayList
@ -33,6 +34,7 @@ object APIHolder {
DubbedAnimeProvider(),
HDMProvider(),
LookMovieProvider(),
TrailersToProvider(),
)
fun getApiFromName(apiName: String?): MainAPI {
@ -61,7 +63,7 @@ abstract class MainAPI {
return null
}
open fun quickSearch(query: String) : ArrayList<SearchResponse>? {
open fun quickSearch(query: String): ArrayList<SearchResponse>? {
return null
}
@ -75,23 +77,22 @@ abstract class MainAPI {
}
}
fun parseRating(ratingString : String?) : Int? {
if(ratingString == null) return null
fun parseRating(ratingString: String?): Int? {
if (ratingString == null) return null
val floatRating = ratingString.toFloatOrNull() ?: return null
return (floatRating * 10).toInt()
}
fun MainAPI.fixUrl(url: String): String {
if(url.startsWith("http")) {
if (url.startsWith("http")) {
return url
}
val startsWithNoHttp = url.startsWith("//")
if(startsWithNoHttp) {
if (startsWithNoHttp) {
return "https:$url"
}
else {
if(url.startsWith('/')) {
} else {
if (url.startsWith('/')) {
return mainUrl + url
}
return "$mainUrl/$url"
@ -176,7 +177,10 @@ interface LoadResponse {
val posterUrl: String?
val year: Int?
val plot: String?
val rating : Int? // 0-100
val rating: Int? // 0-100
val tags: ArrayList<String>?
val duration: String?
val trailerUrl: String?
}
fun LoadResponse?.isEpisodeBased(): Boolean {
@ -189,7 +193,7 @@ fun LoadResponse?.isAnimeBased(): Boolean {
return (this.type == TvType.Anime || this.type == TvType.ONA) // && (this is AnimeLoadResponse)
}
data class AnimeEpisode(val url: String, val name : String? = null)
data class AnimeEpisode(val url: String, val name: String? = null)
data class AnimeLoadResponse(
val engName: String?,
@ -207,12 +211,14 @@ data class AnimeLoadResponse(
val showStatus: ShowStatus?,
override val plot: String?,
val tags: ArrayList<String>? = null,
override val tags: ArrayList<String>? = null,
val synonyms: ArrayList<String>? = null,
val malId: Int? = null,
val anilistId: Int? = null,
override val rating: Int? = null,
override val duration: String? = null,
override val trailerUrl: String? = null,
) : LoadResponse
data class MovieLoadResponse(
@ -226,11 +232,23 @@ data class MovieLoadResponse(
override val year: Int?,
override val plot: String?,
val imdbId: Int?,
val imdbUrl: String?,
override val rating: Int? = null,
override val tags: ArrayList<String>? = null,
override val duration: String? = null,
override val trailerUrl: String? = null,
) : LoadResponse
data class TvSeriesEpisode(val name: String?, val season: Int?, val episode: Int?, val data: String)
data class TvSeriesEpisode(
val name: String?,
val season: Int?,
val episode: Int?,
val data: String,
val posterUrl: String? = null,
val date: String? = null,
val rating: Int? = null,
val descript: String? = null,
)
data class TvSeriesLoadResponse(
override val name: String,
@ -244,6 +262,9 @@ data class TvSeriesLoadResponse(
override val plot: String?,
val showStatus: ShowStatus?,
val imdbId: Int?,
val imdbUrl: String?,
override val rating: Int? = null,
override val tags: ArrayList<String>? = null,
override val duration: String? = null,
override val trailerUrl: String? = null,
) : LoadResponse

View file

@ -6,9 +6,11 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.APIHolder.unixTime
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
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
@ -62,7 +64,7 @@ class LookMovieProvider : MainAPI() {
@JsonProperty("season") var season: String,
)
override fun quickSearch(query: String): ArrayList<SearchResponse>? {
override fun quickSearch(query: String): ArrayList<SearchResponse> {
val movieUrl = "$mainUrl/api/v1/movies/search/?q=$query"
val movieResponse = khttp.get(movieUrl)
val movies = mapper.readValue<LookMovieSearchResultRoot>(movieResponse.text).result
@ -138,12 +140,13 @@ class LookMovieProvider : MainAPI() {
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
val response = khttp.get(data.replace("\$unixtime", unixTime.toString()))
"\"(.*?)\":\"(.*?)\"".toRegex().findAll(response.text).forEach {
var quality = it.groupValues[1].replace("auto", "Auto")
if (quality != "Auto") quality += "p"
val url = it.groupValues[2]
callback.invoke(ExtractorLink(this.name, "${this.name} - $quality", url, "", getQualityFromName(quality),true))
M3u8Manifest.extractLinks(response.text).forEach {
callback.invoke(ExtractorLink(this.name,
"${this.name} - ${it.second}",
fixUrl(it.first),
"",
getQualityFromName(it.second),
true))
}
return true
}
@ -174,9 +177,6 @@ class LookMovieProvider : MainAPI() {
val root = mapper.readValue<LookMovieTokenRoot>(tokenResponse.text)
val accessToken = root.data?.accessToken ?: return null
//https://lookmovie.io/api/v1/security/show-access?slug=9140554-loki-2021&token=&sk=null&step=1
//https://lookmovie.io/api/v1/security/movie-access?id_movie=11582&token=1&sk=&step=1
if (isMovie) {
return MovieLoadResponse(name,
slug,
@ -198,10 +198,6 @@ class LookMovieProvider : MainAPI() {
return this.replace("$replace:", "\"$replace\":")
}
//https://lookmovie.io/api/v1/security/show-access?slug=9140554-loki-2021&token=&sk=null&step=1
//https://lookmovie.io/manifests/shows/json/TGv3dO0pcwomftMrywOnmw/1624571222/128848/master.m3u8
//https://lookmovie.io/api/v1/shows/episode-subtitles/?id_episode=128848
val json = season
.replace("\'", "\"")
.fixSeasonJson("title")
@ -230,6 +226,5 @@ class LookMovieProvider : MainAPI() {
null,
rating)
}
//watch-heading
}
}

View file

@ -109,7 +109,7 @@ class MeloMovieProvider : MainAPI() {
return src.toRegex().find(response)?.groups?.get(1)?.value ?: return null
}
val imdbId = findUsingRegex("var imdb = \"(tt[0-9]*)\"")?.toIntOrNull()
val imdbUrl = findUsingRegex("var imdb = \"(.*?)\"")
val document = Jsoup.parse(response)
val poster = document.selectFirst("img.img-fluid").attr("src")
val type = findUsingRegex("var posttype = ([0-9]*)")?.toInt() ?: return null
@ -128,7 +128,7 @@ class MeloMovieProvider : MainAPI() {
poster,
year,
plot,
imdbId)
imdbUrl)
} else if (type == 2) {
val episodes = ArrayList<TvSeriesEpisode>()
val seasons = document.select("div.accordion__card")
@ -154,7 +154,7 @@ class MeloMovieProvider : MainAPI() {
year,
plot,
null,
imdbId)
imdbUrl)
}
return null
}

View file

@ -0,0 +1,176 @@
package com.lagradost.cloudstream3.movieproviders
import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities
import org.jsoup.Jsoup
// referer = https://trailers.to, USERAGENT ALSO REQUIRED
class TrailersToProvider : MainAPI() {
override val mainUrl: String
get() = "https://trailers.to"
override val name: String
get() = "Trailers.to"
override val hasQuickSearch: Boolean
get() = true
override fun quickSearch(query: String): ArrayList<SearchResponse> {
val url = "https://trailers.to/en/quick-search?q=$query"
val response = khttp.get(url)
val document = Jsoup.parse(response.text)
val items = document.select("div.group-post-minimal > a.post-minimal")
if (items.isNullOrEmpty()) return ArrayList()
val returnValue = ArrayList<SearchResponse>()
for (item in items) {
val href = fixUrl(item.attr("href"))
val poster = item.selectFirst("> div.post-minimal-media > img").attr("src")
val header = item.selectFirst("> div.post-minimal-main")
val name = header.selectFirst("> span.link-black").text()
val info = header.select("> p")
val year = info?.get(1)?.text()?.toIntOrNull()
val isTvShow = href.contains("/tvshow/")
returnValue.add(
if (isTvShow) {
TvSeriesSearchResponse(name, href, href, this.name, TvType.TvSeries, poster, year, null)
} else {
MovieSearchResponse(name, href, href, this.name, TvType.Movie, poster, year)
}
)
}
return returnValue
}
override fun search(query: String): ArrayList<SearchResponse> {
val url = "https://trailers.to/en/popular/movies-tvshows-collections?q=$query"
val response = khttp.get(url)
val document = Jsoup.parse(response.text)
val items = document.select("div.col-lg-8 > article.list-item")
if (items.isNullOrEmpty()) return ArrayList()
val returnValue = ArrayList<SearchResponse>()
for (item in items) {
val poster = item.selectFirst("> div.tour-modern-media > a.tour-modern-figure > img").attr("src")
val infoDiv = item.selectFirst("> div.tour-modern-main")
val nameHeader = infoDiv.select("> h5.tour-modern-title > a").last()
val name = nameHeader.text()
val href = fixUrl(nameHeader.attr("href"))
val year = infoDiv.selectFirst("> div > span.small-text")?.text()?.takeLast(4)?.toIntOrNull()
val isTvShow = href.contains("/tvshow/")
returnValue.add(
if (isTvShow) {
TvSeriesSearchResponse(name, href, href, this.name, TvType.TvSeries, poster, year, null)
} else {
MovieSearchResponse(name, href, href, this.name, TvType.Movie, poster, year)
}
)
}
return returnValue
}
private fun loadLink(data: String, callback: (ExtractorLink) -> Unit): Boolean {
val response = khttp.get(data)
val url = "<source src='(.*?)'".toRegex().find(response.text)?.groupValues?.get(1)
if (url != null) {
callback.invoke(ExtractorLink(this.name, this.name, url, mainUrl, Qualities.Unknown.value, false))
return true
}
return false
}
override fun loadLinks(data: String, isCasting: Boolean, callback: (ExtractorLink) -> Unit): Boolean {
if (isCasting) return false
val isMovie = data.contains("/web-sources/")
if (isMovie) {
return loadLink(data, callback)
} else if (data.contains("/episode/")) {
val response = khttp.get(data)
val document = Jsoup.parse(response.text)
val subData = fixUrl(document.selectFirst("content").attr("data-url") ?: return false)
if (subData.contains("/web-sources/")) {
return loadLink(subData, callback)
}
}
return false
}
override fun load(slug: String): LoadResponse? {
val response = khttp.get(slug)
val document = Jsoup.parse(response.text)
val metaInfo = document.select("div.post-info-meta > ul.post-info-meta-list > li")
val year = metaInfo?.get(0)?.selectFirst("> span.small-text")?.text()?.takeLast(4)?.toIntOrNull()
val rating = parseRating(metaInfo?.get(1)?.selectFirst("> span.small-text")?.text()?.replace("/ 10", ""))
val duration = metaInfo?.get(2)?.selectFirst("> span.small-text")?.text()
val imdbUrl = metaInfo?.get(3)?.selectFirst("> a")?.attr("href")
val trailer = metaInfo?.get(4)?.selectFirst("> a")?.attr("href")
val poster = document.selectFirst("div.slider-image > a > img").attr("src")
val descriptHeader = document.selectFirst("article.post-info")
var title = document.selectFirst("h2.breadcrumbs-custom-title > a").text()
title = title.substring(0, title.length - 6) // REMOVE YEAR
val descript = descriptHeader.select("> div > p").text()
val table = descriptHeader.select("> table.post-info-table > tbody > tr > td")
var generes: List<String>? = null
for (i in 0 until table.size / 2) {
val header = table[i * 2].text()
val info = table[i * 2 + 1]
when (header) {
"Genre" -> generes = info.text().split(",")
}
}
val tags = if (generes == null) null else ArrayList(generes)
val isTvShow = slug.contains("/tvshow/")
if (isTvShow) {
val episodes = document.select("article.tour-modern") ?: return null
val parsedEpisodes = episodes.map { item ->
val epPoster = item.selectFirst("> div.tour-modern-media > a.tour-modern-figure > img").attr("src")
val main = item.selectFirst("> div.tour-modern-main")
val titleHeader = main.selectFirst("> h5.tour-modern-title > a")
val titleName = titleHeader.text()
val href = fixUrl(titleHeader.attr("href"))
val gValues = ".*?Season ([0-9]*).*Episode ([0-9]*): (.*)".toRegex().find(titleName)?.groupValues
val season = gValues?.get(1)?.toIntOrNull()
val episode = gValues?.get(2)?.toIntOrNull()
val epName = gValues?.get(3)
val infoHeaders = main.select("> div > span.small-text")
val date = infoHeaders?.get(0)?.text()
val ratingText = infoHeaders?.get(1)?.text()?.replace("/ 10", "")
val epRating = if (ratingText == null) null else parseRating(ratingText)
val epDescript = main.selectFirst("> p")?.text()
TvSeriesEpisode(epName, season, episode, href, epPoster, date, epRating, epDescript)
}
return TvSeriesLoadResponse(title,
slug,
this.name,
TvType.TvSeries,
ArrayList(parsedEpisodes),
poster,
year,
descript,
null,
imdbUrl,
rating,
tags,
duration,
trailer)
} else {
val data = fixUrl(document.selectFirst("content").attr("data-url") ?: return null)
return MovieLoadResponse(title,
slug,
this.name,
TvType.Movie,
data,
poster,
year,
descript,
imdbUrl,
rating,
tags,
duration,
trailer)
}
}
}

View file

@ -483,6 +483,21 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
result_tag_holder.visibility = GONE
result_status.visibility = GONE
val tags = d.tags
if (tags == null) {
result_tag_holder.visibility = GONE
} else {
result_tag_holder.visibility = VISIBLE
for ((index, tag) in tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
btt.text = tag
result_tag.addView(viewBtt, index)
}
}
when (d.type) {
TvType.Movie -> {
result_play_movie.visibility = VISIBLE
@ -525,20 +540,6 @@ activity?.startActivityForResult(vlcIntent, REQUEST_CODE)
val titleName = d.name
result_title.text = titleName
result_toolbar.title = titleName
if (d.tags == null) {
result_tag_holder.visibility = GONE
} else {
result_tag_holder.visibility = VISIBLE
for ((index, tag) in d.tags.withIndex()) {
val viewBtt = layoutInflater.inflate(R.layout.result_tag, null)
val btt = viewBtt.findViewById<MaterialButton>(R.id.result_tag_card)
btt.text = tag
result_tag.addView(viewBtt, index)
}
}
}
else -> result_title.text = d.name
}

View file

@ -0,0 +1,16 @@
package com.lagradost.cloudstream3.utils.extractors
//{"auto":"/manifests/movies/15559/1624728920/qDwu5BOsfAwfTmnnjmkmXA/master.m3u8","1080p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/1080p/index.m3u8","720p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/720p/index.m3u8","360p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/360p/index.m3u8","480p":"https://vdoc3.sallenes.space/qDwu5BOsfAwfTmnnjmkmXA/1624728920/storage6/movies/the-man-with-the-iron-heart-2017/480p/index.m3u8"}
object M3u8Manifest {
// URL = first, QUALITY = second
fun extractLinks(m3u8Data: String): ArrayList<Pair<String, String>> {
val data: ArrayList<Pair<String, String>> = ArrayList()
"\"(.*?)\":\"(.*?)\"".toRegex().findAll(m3u8Data).forEach {
var quality = it.groupValues[1].replace("auto", "Auto")
if (quality != "Auto") quality += "p"
val url = it.groupValues[2]
data.add(Pair(url, quality))
}
return data
}
}