forked from recloudstream/cloudstream
fix trailers.to tv-series & added notice
This commit is contained in:
parent
54effd6c80
commit
225def6145
7 changed files with 104 additions and 54 deletions
|
@ -40,7 +40,9 @@ android {
|
||||||
|
|
||||||
resValue "string", "app_version",
|
resValue "string", "app_version",
|
||||||
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
"${defaultConfig.versionName}${versionNameSuffix ?: ""}"
|
||||||
|
|
||||||
buildConfigField("String", "BUILDDATE", "new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm\").format(new java.util.Date(" + System.currentTimeMillis() + "L));")
|
buildConfigField("String", "BUILDDATE", "new java.text.SimpleDateFormat(\"yyyy-MM-dd HH:mm\").format(new java.util.Date(" + System.currentTimeMillis() + "L));")
|
||||||
|
buildConfigField("String", "TMDB_API_KEY", "\"" + System.getenv("TMDB_API_KEY") + "\"")
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ object APIHolder {
|
||||||
|
|
||||||
// TmdbProvider(),
|
// TmdbProvider(),
|
||||||
|
|
||||||
// TrailersTwoProvider(),
|
TrailersTwoProvider(),
|
||||||
|
|
||||||
ZoroProvider()
|
ZoroProvider()
|
||||||
)
|
)
|
||||||
|
@ -188,6 +188,7 @@ abstract class MainAPI {
|
||||||
)
|
)
|
||||||
|
|
||||||
open val vpnStatus = VPNStatus.None
|
open val vpnStatus = VPNStatus.None
|
||||||
|
open val providerType = ProviderType.DirectProvider
|
||||||
|
|
||||||
open fun getMainPage(): HomePageResponse? {
|
open fun getMainPage(): HomePageResponse? {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
|
@ -275,6 +276,13 @@ fun imdbUrlToIdNullable(url: String?): String? {
|
||||||
return imdbUrlToId(url)
|
return imdbUrlToId(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class ProviderType {
|
||||||
|
// When data is fetched from a 3rd party site like imdb
|
||||||
|
MetaProvider,
|
||||||
|
// When all data is from the site
|
||||||
|
DirectProvider,
|
||||||
|
}
|
||||||
|
|
||||||
enum class VPNStatus {
|
enum class VPNStatus {
|
||||||
None,
|
None,
|
||||||
MightBeNeeded,
|
MightBeNeeded,
|
||||||
|
|
|
@ -2,20 +2,37 @@ package com.lagradost.cloudstream3.metaproviders
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
|
import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.secondsToReadable
|
||||||
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 java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* episode and season starting from 1
|
||||||
|
* they are null if movie
|
||||||
|
* */
|
||||||
|
data class TmdbLink(
|
||||||
|
@JsonProperty("imdbID") val imdbID: String?,
|
||||||
|
@JsonProperty("tmdbID") val tmdbID: Int?,
|
||||||
|
@JsonProperty("episode") val episode: Int?,
|
||||||
|
@JsonProperty("season") val season: Int?
|
||||||
|
)
|
||||||
|
|
||||||
open class TmdbProvider : MainAPI() {
|
open class TmdbProvider : MainAPI() {
|
||||||
|
|
||||||
|
// Use the LoadResponse from the metadata provider
|
||||||
open val useMetaLoadResponse = false
|
open val useMetaLoadResponse = false
|
||||||
open val apiName = "TMDB"
|
open val apiName = "TMDB"
|
||||||
|
|
||||||
override val hasMainPage: Boolean
|
// As some sites doesn't support s0
|
||||||
get() = true
|
open val disableSeasonZero = true
|
||||||
|
|
||||||
val tmdb = Tmdb("TMDB_KEY_HERE")
|
override val hasMainPage = true
|
||||||
|
override val providerType = ProviderType.MetaProvider
|
||||||
|
|
||||||
|
val tmdb = Tmdb(BuildConfig.TMDB_API_KEY)
|
||||||
|
|
||||||
private fun getImageUrl(link: String?): String? {
|
private fun getImageUrl(link: String?): String? {
|
||||||
if (link == null) return null
|
if (link == null) return null
|
||||||
|
@ -27,18 +44,6 @@ open class TmdbProvider : MainAPI() {
|
||||||
else "https://www.themoviedb.org/movie/${id ?: -1}"
|
else "https://www.themoviedb.org/movie/${id ?: -1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* episode and season starting from 1
|
|
||||||
* they are null if movie
|
|
||||||
* */
|
|
||||||
data class TmdbLink(
|
|
||||||
@JsonProperty("imdbID") val imdbID: String?,
|
|
||||||
@JsonProperty("tmdbID") val tmdbID: Int?,
|
|
||||||
@JsonProperty("episode") val episode: Int?,
|
|
||||||
@JsonProperty("season") val season: Int?
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
private fun BaseTvShow.toSearchResponse(): TvSeriesSearchResponse {
|
private fun BaseTvShow.toSearchResponse(): TvSeriesSearchResponse {
|
||||||
return TvSeriesSearchResponse(
|
return TvSeriesSearchResponse(
|
||||||
this.name ?: this.original_name,
|
this.name ?: this.original_name,
|
||||||
|
@ -73,32 +78,39 @@ open class TmdbProvider : MainAPI() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TvShow.toLoadResponse(): TvSeriesLoadResponse {
|
private fun TvShow.toLoadResponse(): TvSeriesLoadResponse {
|
||||||
val episodes = this.seasons?.mapNotNull {
|
val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 }
|
||||||
it.episodes?.map {
|
?.mapNotNull {
|
||||||
TvSeriesEpisode(
|
it.episodes?.map {
|
||||||
it.name,
|
TvSeriesEpisode(
|
||||||
it.season_number,
|
it.name,
|
||||||
it.episode_number,
|
|
||||||
TmdbLink(
|
|
||||||
it.external_ids?.imdb_id,
|
|
||||||
it.id,
|
|
||||||
it.episode_number,
|
|
||||||
it.season_number,
|
it.season_number,
|
||||||
).toJson(),
|
it.episode_number,
|
||||||
getImageUrl(it.still_path),
|
TmdbLink(
|
||||||
it.air_date?.toString(),
|
it.external_ids?.imdb_id ?: this.external_ids?.imdb_id,
|
||||||
it.rating,
|
this.id,
|
||||||
it.overview,
|
it.episode_number,
|
||||||
)
|
it.season_number,
|
||||||
} ?: (1..(it.episode_count ?: 1)).map { episodeNum ->
|
).toJson(),
|
||||||
TvSeriesEpisode(
|
getImageUrl(it.still_path),
|
||||||
episode = episodeNum,
|
it.air_date?.toString(),
|
||||||
data = episodeNum.toString(),
|
it.rating,
|
||||||
season = it.season_number
|
it.overview,
|
||||||
)
|
)
|
||||||
}
|
} ?: (1..(it.episode_count ?: 1)).map { episodeNum ->
|
||||||
}?.flatten() ?: listOf()
|
TvSeriesEpisode(
|
||||||
|
episode = episodeNum,
|
||||||
|
data = TmdbLink(
|
||||||
|
this.external_ids?.imdb_id,
|
||||||
|
this.id,
|
||||||
|
episodeNum,
|
||||||
|
it.season_number,
|
||||||
|
).toJson(),
|
||||||
|
season = it.season_number
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}?.flatten() ?: listOf()
|
||||||
|
|
||||||
|
// println("STATUS ${this.status}")
|
||||||
return TvSeriesLoadResponse(
|
return TvSeriesLoadResponse(
|
||||||
this.name ?: this.original_name,
|
this.name ?: this.original_name,
|
||||||
getUrl(id, true),
|
getUrl(id, true),
|
||||||
|
@ -112,18 +124,17 @@ open class TmdbProvider : MainAPI() {
|
||||||
}.get(Calendar.YEAR)
|
}.get(Calendar.YEAR)
|
||||||
},
|
},
|
||||||
this.overview,
|
this.overview,
|
||||||
null,//this.status
|
null, // this.status
|
||||||
null, // possible to get
|
this.external_ids?.imdb_id,
|
||||||
this.rating,
|
this.rating,
|
||||||
this.genres?.mapNotNull { it.name },
|
this.genres?.mapNotNull { it.name },
|
||||||
null, //this.episode_run_time.average()
|
this.episode_run_time?.average()?.times(60)?.toInt()?.let { secondsToReadable(it, "") },
|
||||||
null,
|
null,
|
||||||
this.recommendations?.results?.map { it.toSearchResponse() }
|
this.recommendations?.results?.map { it.toSearchResponse() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Movie.toLoadResponse(): MovieLoadResponse {
|
private fun Movie.toLoadResponse(): MovieLoadResponse {
|
||||||
println("EXTERNAL IDS ${this.toJson()}")
|
|
||||||
return MovieLoadResponse(
|
return MovieLoadResponse(
|
||||||
this.title ?: this.original_title,
|
this.title ?: this.original_title,
|
||||||
getUrl(id, true),
|
getUrl(id, true),
|
||||||
|
@ -145,7 +156,7 @@ open class TmdbProvider : MainAPI() {
|
||||||
null,//this.status
|
null,//this.status
|
||||||
this.rating,
|
this.rating,
|
||||||
this.genres?.mapNotNull { it.name },
|
this.genres?.mapNotNull { it.name },
|
||||||
null, //this.episode_run_time.average()
|
this.runtime?.times(60)?.let { secondsToReadable(it, "") },
|
||||||
null,
|
null,
|
||||||
this.recommendations?.results?.map { it.toSearchResponse() }
|
this.recommendations?.results?.map { it.toSearchResponse() }
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,8 +2,11 @@ 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.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import com.lagradost.cloudstream3.ProviderType
|
||||||
import com.lagradost.cloudstream3.SubtitleFile
|
import com.lagradost.cloudstream3.SubtitleFile
|
||||||
|
import com.lagradost.cloudstream3.TvType
|
||||||
import com.lagradost.cloudstream3.mapper
|
import com.lagradost.cloudstream3.mapper
|
||||||
|
import com.lagradost.cloudstream3.metaproviders.TmdbLink
|
||||||
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
import com.lagradost.cloudstream3.metaproviders.TmdbProvider
|
||||||
import com.lagradost.cloudstream3.network.get
|
import com.lagradost.cloudstream3.network.get
|
||||||
import com.lagradost.cloudstream3.network.text
|
import com.lagradost.cloudstream3.network.text
|
||||||
|
@ -30,6 +33,15 @@ class TrailersTwoProvider : TmdbProvider() {
|
||||||
override val instantLinkLoading: Boolean
|
override val instantLinkLoading: Boolean
|
||||||
get() = true
|
get() = true
|
||||||
|
|
||||||
|
override val supportedTypes: Set<TvType>
|
||||||
|
get() = setOf(
|
||||||
|
TvType.Movie,
|
||||||
|
TvType.TvSeries,
|
||||||
|
TvType.AnimeMovie,
|
||||||
|
TvType.Anime,
|
||||||
|
TvType.Cartoon
|
||||||
|
)
|
||||||
|
|
||||||
override fun loadLinks(
|
override fun loadLinks(
|
||||||
data: String,
|
data: String,
|
||||||
isCasting: Boolean,
|
isCasting: Boolean,
|
||||||
|
@ -37,8 +49,10 @@ class TrailersTwoProvider : TmdbProvider() {
|
||||||
callback: (ExtractorLink) -> Unit
|
callback: (ExtractorLink) -> Unit
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val mappedData = mapper.readValue<TmdbLink>(data)
|
val mappedData = mapper.readValue<TmdbLink>(data)
|
||||||
println("MAPPED $mappedData")
|
val (id, site) = if (mappedData.imdbID != null) listOf(
|
||||||
if (mappedData.imdbID == null) return false
|
mappedData.imdbID,
|
||||||
|
"imdb"
|
||||||
|
) 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 subtitleUrl = if (isMovie) {
|
||||||
|
@ -46,25 +60,25 @@ class TrailersTwoProvider : TmdbProvider() {
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
this.name,
|
this.name,
|
||||||
this.name,
|
this.name,
|
||||||
"https://trailers.to/video/$user/imdb/${mappedData.imdbID}",
|
"https://trailers.to/video/$user/$site/$id",
|
||||||
"https://trailers.to",
|
"https://trailers.to",
|
||||||
Qualities.Unknown.value,
|
Qualities.Unknown.value,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"https://trailers.to/subtitles/$user/imdb/${mappedData.imdbID}"
|
"https://trailers.to/subtitles/$user/$site/$id"
|
||||||
} else {
|
} else {
|
||||||
callback.invoke(
|
callback.invoke(
|
||||||
ExtractorLink(
|
ExtractorLink(
|
||||||
this.name,
|
this.name,
|
||||||
this.name,
|
this.name,
|
||||||
"https://trailers.to/video/$user/imdb/${mappedData.imdbID}/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}",
|
"https://trailers.to/video/$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}",
|
||||||
"https://trailers.to",
|
"https://trailers.to",
|
||||||
Qualities.Unknown.value,
|
Qualities.Unknown.value,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
"https://trailers.to/subtitles/$user/imdb/${mappedData.imdbID}/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}"
|
"https://trailers.to/subtitles/$user/$site/$id/S${mappedData.season ?: 1}E${mappedData.episode ?: 1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
val subtitles =
|
val subtitles =
|
||||||
|
@ -73,9 +87,9 @@ class TrailersTwoProvider : TmdbProvider() {
|
||||||
subtitlesMapped.forEach {
|
subtitlesMapped.forEach {
|
||||||
subtitleCallback.invoke(
|
subtitleCallback.invoke(
|
||||||
SubtitleFile(
|
SubtitleFile(
|
||||||
it.LanguageCode ?: "en",
|
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"}"
|
||||||
).also { println(it) }
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -927,6 +927,12 @@ class ResultFragment : Fragment() {
|
||||||
}
|
}
|
||||||
result_vpn?.visibility = if (api.vpnStatus == VPNStatus.None) GONE else VISIBLE
|
result_vpn?.visibility = if (api.vpnStatus == VPNStatus.None) GONE else VISIBLE
|
||||||
|
|
||||||
|
result_info?.text = when (api.providerType){
|
||||||
|
ProviderType.MetaProvider -> getString(R.string.provider_info_meta)
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
result_info?.isVisible = api.providerType == ProviderType.MetaProvider
|
||||||
|
|
||||||
//result_bookmark_button.text = getString(R.string.type_watching)
|
//result_bookmark_button.text = getString(R.string.type_watching)
|
||||||
|
|
||||||
currentHeaderName = d.name
|
currentHeaderName = d.name
|
||||||
|
|
|
@ -275,6 +275,12 @@
|
||||||
tools:text="@string/vpn_torrent"
|
tools:text="@string/vpn_torrent"
|
||||||
android:layout_width="match_parent" android:layout_height="wrap_content">
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
</TextView>
|
</TextView>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/result_info"
|
||||||
|
android:textSize="15sp"
|
||||||
|
tools:text="@string/provider_info_meta"
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
</TextView>
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|
|
@ -153,7 +153,10 @@
|
||||||
<string name="action_open_watching">More Info</string>
|
<string name="action_open_watching">More Info</string>
|
||||||
|
|
||||||
<string name="vpn_might_be_needed">A VPN might be needed for this provider to work correctly</string>
|
<string name="vpn_might_be_needed">A VPN might be needed for this provider to work correctly</string>
|
||||||
<string name="vpn_torrent">This providers is a torrent, a VPN is recommended</string>
|
<string name="vpn_torrent">This provider is a torrent, a VPN is recommended</string>
|
||||||
|
|
||||||
|
<string name="provider_info_meta">Metadata is not provided by site, video loading might fail.</string>
|
||||||
|
|
||||||
<string name="torrent_plot">Description</string>
|
<string name="torrent_plot">Description</string>
|
||||||
<string name="normal_no_plot">No Plot Found</string>
|
<string name="normal_no_plot">No Plot Found</string>
|
||||||
<string name="torrent_no_plot">No Description Found</string>
|
<string name="torrent_no_plot">No Description Found</string>
|
||||||
|
|
Loading…
Reference in a new issue