forked from recloudstream/cloudstream
trailers.to backup fix for movies
This commit is contained in:
parent
dcb97a1f63
commit
06bafa9801
6 changed files with 242 additions and 63 deletions
|
@ -5,6 +5,7 @@ import com.lagradost.cloudstream3.*
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.toJson
|
||||
import com.uwetrottmann.tmdb2.Tmdb
|
||||
import com.uwetrottmann.tmdb2.entities.*
|
||||
import retrofit2.awaitResponse
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
@ -15,7 +16,8 @@ data class TmdbLink(
|
|||
@JsonProperty("imdbID") val imdbID: String?,
|
||||
@JsonProperty("tmdbID") val tmdbID: 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() {
|
||||
|
@ -144,6 +146,7 @@ open class TmdbProvider : MainAPI() {
|
|||
this.id,
|
||||
null,
|
||||
null,
|
||||
this.title ?: this.original_title,
|
||||
).toJson(),
|
||||
getImageUrl(this.poster_path),
|
||||
this.release_date?.let {
|
||||
|
@ -173,23 +176,33 @@ open class TmdbProvider : MainAPI() {
|
|||
// it.toSearchResponse()
|
||||
// } ?: 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()
|
||||
} ?: listOf()
|
||||
|
||||
val discoverSeries = tmdb.discoverTv().build().execute().body()?.results?.map {
|
||||
}, {
|
||||
discoverSeries = tmdb.discoverTv().build().awaitResponse().body()?.results?.map {
|
||||
it.toSearchResponse()
|
||||
} ?: listOf()
|
||||
|
||||
}, {
|
||||
// https://en.wikipedia.org/wiki/ISO_3166-1
|
||||
val topMovies =
|
||||
tmdb.moviesService().topRated(1, "en-US", "US").execute().body()?.results?.map {
|
||||
topMovies =
|
||||
tmdb.moviesService().topRated(1, "en-US", "US").awaitResponse()
|
||||
.body()?.results?.map {
|
||||
it.toSearchResponse()
|
||||
} ?: 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()
|
||||
} ?: listOf()
|
||||
}
|
||||
)
|
||||
|
||||
return HomePageResponse(
|
||||
listOf(
|
||||
|
@ -232,19 +245,19 @@ open class TmdbProvider : MainAPI() {
|
|||
|
||||
return if (useMetaLoadResponse) {
|
||||
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()
|
||||
} else {
|
||||
val body = tmdb.moviesService().summary(id, "en-US").execute().body()
|
||||
val body = tmdb.moviesService().summary(id, "en-US").awaitResponse().body()
|
||||
body?.toLoadResponse()
|
||||
}
|
||||
} else {
|
||||
loadFromTmdb(id)?.let { return it }
|
||||
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 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())
|
||||
?: loadFromTmdb(id, details?.seasons ?: listOf())
|
||||
} else {
|
||||
|
@ -254,16 +267,14 @@ open class TmdbProvider : MainAPI() {
|
|||
result
|
||||
}
|
||||
} else {
|
||||
tmdb.moviesService().externalIds(id, "en-US").execute()
|
||||
tmdb.moviesService().externalIds(id, "en-US").awaitResponse()
|
||||
.body()?.imdb_id?.let { loadFromImdb(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
it.movie?.toSearchResponse() ?: it.tvShow?.toSearchResponse()
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package com.lagradost.cloudstream3.movieproviders
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.lagradost.cloudstream3.SubtitleFile
|
||||
import com.lagradost.cloudstream3.TvType
|
||||
import com.lagradost.cloudstream3.app
|
||||
import com.lagradost.cloudstream3.mapper
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbLink
|
||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.parseJson
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
|
@ -20,6 +17,105 @@ class TrailersTwoProvider : TmdbProvider() {
|
|||
override val useMetaLoadResponse = 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(
|
||||
TvType.Movie,
|
||||
TvType.TvSeries,
|
||||
|
@ -34,50 +130,99 @@ class TrailersTwoProvider : TmdbProvider() {
|
|||
subtitleCallback: (SubtitleFile) -> Unit,
|
||||
callback: (ExtractorLink) -> Unit
|
||||
): Boolean {
|
||||
val mappedData = mapper.readValue<TmdbLink>(data)
|
||||
val mappedData = parseJson<TmdbLink>(data)
|
||||
val (id, site) = if (mappedData.imdbID != null) listOf(
|
||||
mappedData.imdbID,
|
||||
"imdb"
|
||||
) else listOf(mappedData.tmdbID.toString(), "tmdb")
|
||||
|
||||
val isMovie = mappedData.episode == null && mappedData.season == null
|
||||
val subtitleUrl = if (isMovie) {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
"https://trailers.to/video/$user/$site/$id",
|
||||
"https://trailers.to",
|
||||
Qualities.Unknown.value,
|
||||
false,
|
||||
val (videoUrl, subtitleUrl) = if (isMovie) {
|
||||
val suffix = "$user/$site/$id"
|
||||
Pair(
|
||||
"https://trailers.to/video/$suffix",
|
||||
"https://trailers.to/subtitles/$suffix"
|
||||
)
|
||||
)
|
||||
"https://trailers.to/subtitles/$user/$site/$id"
|
||||
} else {
|
||||
callback.invoke(
|
||||
ExtractorLink(
|
||||
this.name,
|
||||
this.name,
|
||||
"https://trailers.to/video/$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}",
|
||||
"https://trailers.to",
|
||||
Qualities.Unknown.value,
|
||||
false,
|
||||
val suffix = "$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}"
|
||||
Pair(
|
||||
"https://trailers.to/video/$suffix",
|
||||
"https://trailers.to/subtitles/$suffix"
|
||||
)
|
||||
)
|
||||
"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 =
|
||||
app.get(subtitleUrl).text
|
||||
val subtitlesMapped = mapper.readValue<List<TrailersSubtitleFile>>(subtitles)
|
||||
val subtitlesMapped = parseJson<List<TrailersSubtitleFile>>(subtitles)
|
||||
subtitlesMapped.forEach {
|
||||
subtitleCallback.invoke(
|
||||
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/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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -293,6 +293,10 @@ open class Requests {
|
|||
.followRedirects(allowRedirects)
|
||||
.followSslRedirects(allowRedirects)
|
||||
.callTimeout(timeout, TimeUnit.SECONDS)
|
||||
if (timeout > 0)
|
||||
client
|
||||
.connectTimeout(timeout, TimeUnit.SECONDS)
|
||||
.readTimeout(timeout, TimeUnit.SECONDS)
|
||||
|
||||
if (interceptor != null) client.addInterceptor(interceptor)
|
||||
val request =
|
||||
|
|
|
@ -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 -> {
|
||||
showToast(activity, exception.message, Toast.LENGTH_SHORT)
|
||||
}
|
||||
|
|
|
@ -554,6 +554,15 @@ class CS3IPlayer : IPlayer {
|
|||
updatedTime()
|
||||
if (!hasUsedFirstRender) { // this insures that we only call this once per player load
|
||||
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)
|
||||
hasUsedFirstRender = true
|
||||
val format = exoPlayer?.videoFormat
|
||||
|
|
|
@ -44,6 +44,8 @@ enum class CSPlayerLoading {
|
|||
//IsDone,
|
||||
}
|
||||
|
||||
class InvalidFileException(msg : String) : Exception(msg)
|
||||
|
||||
//http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
|
||||
const val STATE_RESUME_WINDOW = "resumeWindow"
|
||||
const val STATE_RESUME_POSITION = "resumePosition"
|
||||
|
|
Loading…
Reference in a new issue