trailers.to backup fix for movies

This commit is contained in:
LagradOst 2022-01-31 20:39:24 +01:00
parent dcb97a1f63
commit 06bafa9801
6 changed files with 242 additions and 63 deletions

View file

@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.lagradost.cloudstream3.utils.AppUtils.toJson
import com.uwetrottmann.tmdb2.Tmdb import com.uwetrottmann.tmdb2.Tmdb
import com.uwetrottmann.tmdb2.entities.* import com.uwetrottmann.tmdb2.entities.*
import retrofit2.awaitResponse
import java.util.* import java.util.*
/** /**
@ -15,7 +16,8 @@ data class TmdbLink(
@JsonProperty("imdbID") val imdbID: String?, @JsonProperty("imdbID") val imdbID: String?,
@JsonProperty("tmdbID") val tmdbID: Int?, @JsonProperty("tmdbID") val tmdbID: Int?,
@JsonProperty("episode") val episode: Int?, @JsonProperty("episode") val episode: Int?,
@JsonProperty("season") val season: Int? @JsonProperty("season") val season: Int?,
@JsonProperty("movieName") val movieName: String? = null,
) )
open class TmdbProvider : MainAPI() { open class TmdbProvider : MainAPI() {
@ -144,6 +146,7 @@ open class TmdbProvider : MainAPI() {
this.id, this.id,
null, null,
null, null,
this.title ?: this.original_title,
).toJson(), ).toJson(),
getImageUrl(this.poster_path), getImageUrl(this.poster_path),
this.release_date?.let { this.release_date?.let {
@ -173,23 +176,33 @@ open class TmdbProvider : MainAPI() {
// it.toSearchResponse() // it.toSearchResponse()
// } ?: listOf() // } ?: listOf()
val discoverMovies = tmdb.discoverMovie().build().execute().body()?.results?.map { var discoverMovies: List<MovieSearchResponse> = listOf()
var discoverSeries: List<TvSeriesSearchResponse> = listOf()
var topMovies: List<MovieSearchResponse> = listOf()
var topSeries: List<TvSeriesSearchResponse> = listOf()
argamap(
{
discoverMovies = tmdb.discoverMovie().build().awaitResponse().body()?.results?.map {
it.toSearchResponse() it.toSearchResponse()
} ?: listOf() } ?: listOf()
}, {
val discoverSeries = tmdb.discoverTv().build().execute().body()?.results?.map { discoverSeries = tmdb.discoverTv().build().awaitResponse().body()?.results?.map {
it.toSearchResponse() it.toSearchResponse()
} ?: listOf() } ?: listOf()
}, {
// https://en.wikipedia.org/wiki/ISO_3166-1 // https://en.wikipedia.org/wiki/ISO_3166-1
val topMovies = topMovies =
tmdb.moviesService().topRated(1, "en-US", "US").execute().body()?.results?.map { tmdb.moviesService().topRated(1, "en-US", "US").awaitResponse()
.body()?.results?.map {
it.toSearchResponse() it.toSearchResponse()
} ?: listOf() } ?: listOf()
}, {
val topSeries = tmdb.tvService().topRated(1, "en-US").execute().body()?.results?.map { topSeries =
tmdb.tvService().topRated(1, "en-US").awaitResponse().body()?.results?.map {
it.toSearchResponse() it.toSearchResponse()
} ?: listOf() } ?: listOf()
}
)
return HomePageResponse( return HomePageResponse(
listOf( listOf(
@ -232,19 +245,19 @@ open class TmdbProvider : MainAPI() {
return if (useMetaLoadResponse) { return if (useMetaLoadResponse) {
return if (isTvSeries) { return if (isTvSeries) {
val body = tmdb.tvService().tv(id, "en-US").execute().body() val body = tmdb.tvService().tv(id, "en-US").awaitResponse().body()
body?.toLoadResponse() body?.toLoadResponse()
} else { } else {
val body = tmdb.moviesService().summary(id, "en-US").execute().body() val body = tmdb.moviesService().summary(id, "en-US").awaitResponse().body()
body?.toLoadResponse() body?.toLoadResponse()
} }
} else { } else {
loadFromTmdb(id)?.let { return it } loadFromTmdb(id)?.let { return it }
if (isTvSeries) { if (isTvSeries) {
tmdb.tvService().externalIds(id, "en-US").execute().body()?.imdb_id?.let { tmdb.tvService().externalIds(id, "en-US").awaitResponse().body()?.imdb_id?.let {
val fromImdb = loadFromImdb(it) val fromImdb = loadFromImdb(it)
val result = if (fromImdb == null) { val result = if (fromImdb == null) {
val details = tmdb.tvService().tv(id, "en-US").execute().body() val details = tmdb.tvService().tv(id, "en-US").awaitResponse().body()
loadFromImdb(it, details?.seasons ?: listOf()) loadFromImdb(it, details?.seasons ?: listOf())
?: loadFromTmdb(id, details?.seasons ?: listOf()) ?: loadFromTmdb(id, details?.seasons ?: listOf())
} else { } else {
@ -254,16 +267,14 @@ open class TmdbProvider : MainAPI() {
result result
} }
} else { } else {
tmdb.moviesService().externalIds(id, "en-US").execute() tmdb.moviesService().externalIds(id, "en-US").awaitResponse()
.body()?.imdb_id?.let { loadFromImdb(it) } .body()?.imdb_id?.let { loadFromImdb(it) }
} }
} }
} }
override suspend fun search(query: String): List<SearchResponse>? { override suspend fun search(query: String): List<SearchResponse>? {
return tmdb.searchService().multi(query, 1, "en-Us", "US", true).execute() return tmdb.searchService().multi(query, 1, "en-Us", "US", true).awaitResponse()
.body()?.results?.mapNotNull { .body()?.results?.mapNotNull {
it.movie?.toSearchResponse() ?: it.tvShow?.toSearchResponse() it.movie?.toSearchResponse() ?: it.tvShow?.toSearchResponse()
} }

View file

@ -1,13 +1,10 @@
package com.lagradost.cloudstream3.movieproviders package com.lagradost.cloudstream3.movieproviders
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.module.kotlin.readValue import com.lagradost.cloudstream3.*
import com.lagradost.cloudstream3.SubtitleFile
import com.lagradost.cloudstream3.TvType
import com.lagradost.cloudstream3.app
import com.lagradost.cloudstream3.mapper
import com.lagradost.cloudstream3.metaproviders.TmdbLink import com.lagradost.cloudstream3.metaproviders.TmdbLink
import com.lagradost.cloudstream3.metaproviders.TmdbProvider import com.lagradost.cloudstream3.metaproviders.TmdbProvider
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
import com.lagradost.cloudstream3.utils.ExtractorLink import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.Qualities import com.lagradost.cloudstream3.utils.Qualities
import com.lagradost.cloudstream3.utils.SubtitleHelper import com.lagradost.cloudstream3.utils.SubtitleHelper
@ -20,6 +17,105 @@ class TrailersTwoProvider : TmdbProvider() {
override val useMetaLoadResponse = true override val useMetaLoadResponse = true
override val instantLinkLoading = true override val instantLinkLoading = true
data class TrailersEpisode(
// val tvShowItemID: Long?,
//val tvShow: String,
//val tvShowIMDB: String?,
//val tvShowTMDB: Long?,
@JsonProperty("ItemID")
val itemID: Int,
//val title: String,
//@JsonProperty("IMDb")
@JsonProperty("IMDb")
val imdb: String?,
//@JsonProperty("TMDb")
@JsonProperty("TMDb")
val tmdb: Int?,
//val releaseDate: String,
//val entryDate: String
)
data class TrailersMovie(
@JsonProperty("ItemID")
val itemID: Int,
@JsonProperty("IMDb")
val imdb: String?,
@JsonProperty("TMDb")
val tmdb: Int?,
//@JsonProperty("Title")
//val title: String?,
)
/*companion object {
private var tmdbToIdMovies: HashMap<Int, Int> = hashMapOf()
private var imdbToIdMovies: HashMap<String, Int> = hashMapOf()
private var tmdbToIdTvSeries: HashMap<Int, Int> = hashMapOf()
private var imdbToIdTvSeries: HashMap<String, Int> = hashMapOf()
private const val startDate = 1900
private const val endDate = 9999
fun getEpisode(tmdb: Int?, imdb: String?): Int? {
var currentId: Int? = null
if (tmdb != null) {
currentId = tmdbToIdTvSeries[tmdb]
}
if (imdb != null && currentId == null) {
currentId = imdbToIdTvSeries[imdb]
}
return currentId
}
fun getMovie(tmdb: Int?, imdb: String?): Int? {
var currentId: Int? = null
if (tmdb != null) {
currentId = tmdbToIdMovies[tmdb]
}
if (imdb != null && currentId == null) {
currentId = imdbToIdMovies[imdb]
}
return currentId
}
suspend fun fillData(isMovie: Boolean) {
if (isMovie) {
if (tmdbToIdMovies.isNotEmpty() || imdbToIdMovies.isNotEmpty()) {
return
}
parseJson<List<TrailersMovie>>(
app.get(
"https://trailers.to/movies?from=$startDate-01-01&to=$endDate",
timeout = 30
).text
).forEach { movie ->
movie.imdb?.let {
imdbToIdTvSeries[it] = movie.itemID
}
movie.tmdb?.let {
tmdbToIdTvSeries[it] = movie.itemID
}
}
} else {
if (tmdbToIdTvSeries.isNotEmpty() || imdbToIdTvSeries.isNotEmpty()) {
return
}
parseJson<List<TrailersEpisode>>(
app.get(
"https://trailers.to/episodes?from=$startDate-01-01&to=$endDate",
timeout = 30
).text
).forEach { episode ->
episode.imdb?.let {
imdbToIdTvSeries[it] = episode.itemID
}
episode.tmdb?.let {
tmdbToIdTvSeries[it] = episode.itemID
}
}
}
}
}*/
override val supportedTypes = setOf( override val supportedTypes = setOf(
TvType.Movie, TvType.Movie,
TvType.TvSeries, TvType.TvSeries,
@ -34,50 +130,99 @@ class TrailersTwoProvider : TmdbProvider() {
subtitleCallback: (SubtitleFile) -> Unit, subtitleCallback: (SubtitleFile) -> Unit,
callback: (ExtractorLink) -> Unit callback: (ExtractorLink) -> Unit
): Boolean { ): Boolean {
val mappedData = mapper.readValue<TmdbLink>(data) val mappedData = parseJson<TmdbLink>(data)
val (id, site) = if (mappedData.imdbID != null) listOf( val (id, site) = if (mappedData.imdbID != null) listOf(
mappedData.imdbID, mappedData.imdbID,
"imdb" "imdb"
) else listOf(mappedData.tmdbID.toString(), "tmdb") ) else listOf(mappedData.tmdbID.toString(), "tmdb")
val isMovie = mappedData.episode == null && mappedData.season == null val isMovie = mappedData.episode == null && mappedData.season == null
val subtitleUrl = if (isMovie) { val (videoUrl, subtitleUrl) = if (isMovie) {
callback.invoke( val suffix = "$user/$site/$id"
ExtractorLink( Pair(
this.name, "https://trailers.to/video/$suffix",
this.name, "https://trailers.to/subtitles/$suffix"
"https://trailers.to/video/$user/$site/$id",
"https://trailers.to",
Qualities.Unknown.value,
false,
) )
)
"https://trailers.to/subtitles/$user/$site/$id"
} else { } else {
callback.invoke( val suffix = "$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}"
ExtractorLink( Pair(
this.name, "https://trailers.to/video/$suffix",
this.name, "https://trailers.to/subtitles/$suffix"
"https://trailers.to/video/$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}",
"https://trailers.to",
Qualities.Unknown.value,
false,
) )
)
"https://trailers.to/subtitles/$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}"
} }
callback.invoke(
ExtractorLink(
this.name,
this.name,
videoUrl,
"https://trailers.to",
Qualities.Unknown.value,
false,
)
)
argamap(
{
val subtitles = val subtitles =
app.get(subtitleUrl).text app.get(subtitleUrl).text
val subtitlesMapped = mapper.readValue<List<TrailersSubtitleFile>>(subtitles) val subtitlesMapped = parseJson<List<TrailersSubtitleFile>>(subtitles)
subtitlesMapped.forEach { subtitlesMapped.forEach {
subtitleCallback.invoke( subtitleCallback.invoke(
SubtitleFile( SubtitleFile(
SubtitleHelper.fromTwoLettersToLanguage(it.LanguageCode ?: "en") ?: "English", SubtitleHelper.fromTwoLettersToLanguage(it.LanguageCode ?: "en")
?: "English",
"https://trailers.to/subtitles/${it.ContentHash ?: return@forEach}/${it.LanguageCode ?: return@forEach}.vtt" // ${it.MetaInfo?.SubFormat ?: "srt"}" "https://trailers.to/subtitles/${it.ContentHash ?: return@forEach}/${it.LanguageCode ?: return@forEach}.vtt" // ${it.MetaInfo?.SubFormat ?: "srt"}"
) )
) )
} }
}, {
//https://trailers.to/en/quick-search?q=iron man
val name = mappedData.movieName
if (name != null && isMovie) {
app.get("https://trailers.to/en/quick-search?q=${name}").document.select("a.post-minimal")
.mapNotNull {
it?.attr("href")
}.map { Regex("""/movie/(\d+)/""").find(it)?.groupValues?.getOrNull(1) }
.firstOrNull()?.let { movieId ->
val correctUrl = app.get(videoUrl).url
callback.invoke(
ExtractorLink(
this.name,
"${this.name} Backup",
correctUrl.replace("/$user/0/", "/$user/$movieId/"),
"https://trailers.to",
Qualities.Unknown.value,
false,
)
)
}
}
}
)
/*
// the problem with this code is that it tages ages and the json file is 50mb or so for movies
fillData(isMovie)
val movieId = if (isMovie) {
getMovie(mappedData.tmdbID, mappedData.imdbID)
} else {
getEpisode(mappedData.tmdbID, mappedData.imdbID)
} ?: return@argamap
val request = app.get(data)
val endUrl = request.url
callback.invoke(
ExtractorLink(
this.name,
"${this.name} Backup",
endUrl.replace("/cloudstream/0/", "/cloudstream/$movieId/"),
"https://trailers.to",
Qualities.Unknown.value,
false,
)
)
*/
return true return true
} }
} }

View file

@ -293,6 +293,10 @@ open class Requests {
.followRedirects(allowRedirects) .followRedirects(allowRedirects)
.followSslRedirects(allowRedirects) .followSslRedirects(allowRedirects)
.callTimeout(timeout, TimeUnit.SECONDS) .callTimeout(timeout, TimeUnit.SECONDS)
if (timeout > 0)
client
.connectTimeout(timeout, TimeUnit.SECONDS)
.readTimeout(timeout, TimeUnit.SECONDS)
if (interceptor != null) client.addInterceptor(interceptor) if (interceptor != null) client.addInterceptor(interceptor)
val request = val request =

View file

@ -236,6 +236,14 @@ abstract class AbstractPlayerFragment(
} }
} }
} }
is InvalidFileException -> {
showToast(
activity,
"${ctx.getString(R.string.source_error)}\n${exception.message}",
Toast.LENGTH_SHORT
)
nextMirror()
}
else -> { else -> {
showToast(activity, exception.message, Toast.LENGTH_SHORT) showToast(activity, exception.message, Toast.LENGTH_SHORT)
} }

View file

@ -554,6 +554,15 @@ class CS3IPlayer : IPlayer {
updatedTime() updatedTime()
if (!hasUsedFirstRender) { // this insures that we only call this once per player load if (!hasUsedFirstRender) { // this insures that we only call this once per player load
Log.i(TAG, "Rendered first frame") Log.i(TAG, "Rendered first frame")
val invalid = exoPlayer?.duration?.let { duration ->
duration < 20000L
} ?: false
if(invalid) {
playerError?.invoke(InvalidFileException("Too short playback"))
return
}
setPreferredSubtitles(currentSubtitles) setPreferredSubtitles(currentSubtitles)
hasUsedFirstRender = true hasUsedFirstRender = true
val format = exoPlayer?.videoFormat val format = exoPlayer?.videoFormat

View file

@ -44,6 +44,8 @@ enum class CSPlayerLoading {
//IsDone, //IsDone,
} }
class InvalidFileException(msg : String) : Exception(msg)
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 //http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
const val STATE_RESUME_WINDOW = "resumeWindow" const val STATE_RESUME_WINDOW = "resumeWindow"
const val STATE_RESUME_POSITION = "resumePosition" const val STATE_RESUME_POSITION = "resumePosition"