diff --git a/app/build.gradle b/app/build.gradle index e1b20711..1a295517 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,9 @@ android { resValue "string", "app_version", "${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", "TMDB_API_KEY", "\"" + System.getenv("TMDB_API_KEY") + "\"") testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt index 0811e43e..a92a3bbd 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainAPI.kt @@ -55,7 +55,7 @@ object APIHolder { // TmdbProvider(), -// TrailersTwoProvider(), + TrailersTwoProvider(), ZoroProvider() ) @@ -188,6 +188,7 @@ abstract class MainAPI { ) open val vpnStatus = VPNStatus.None + open val providerType = ProviderType.DirectProvider open fun getMainPage(): HomePageResponse? { throw NotImplementedError() @@ -275,6 +276,13 @@ fun imdbUrlToIdNullable(url: String?): String? { 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 { None, MightBeNeeded, diff --git a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt index f044ede6..2f6e4dc1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/metaproviders/TmdbProvider.kt @@ -2,20 +2,37 @@ package com.lagradost.cloudstream3.metaproviders import com.fasterxml.jackson.annotation.JsonProperty import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.secondsToReadable import com.lagradost.cloudstream3.utils.AppUtils.toJson import com.uwetrottmann.tmdb2.Tmdb import com.uwetrottmann.tmdb2.entities.* 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() { + // Use the LoadResponse from the metadata provider open val useMetaLoadResponse = false open val apiName = "TMDB" - override val hasMainPage: Boolean - get() = true + // As some sites doesn't support s0 + 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? { if (link == null) return null @@ -27,18 +44,6 @@ open class TmdbProvider : MainAPI() { 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 { return TvSeriesSearchResponse( this.name ?: this.original_name, @@ -73,32 +78,39 @@ open class TmdbProvider : MainAPI() { } private fun TvShow.toLoadResponse(): TvSeriesLoadResponse { - val episodes = this.seasons?.mapNotNull { - it.episodes?.map { - TvSeriesEpisode( - it.name, - it.season_number, - it.episode_number, - TmdbLink( - it.external_ids?.imdb_id, - it.id, - it.episode_number, + val episodes = this.seasons?.filter { !disableSeasonZero || (it.season_number ?: 0) != 0 } + ?.mapNotNull { + it.episodes?.map { + TvSeriesEpisode( + it.name, it.season_number, - ).toJson(), - getImageUrl(it.still_path), - it.air_date?.toString(), - it.rating, - it.overview, - ) - } ?: (1..(it.episode_count ?: 1)).map { episodeNum -> - TvSeriesEpisode( - episode = episodeNum, - data = episodeNum.toString(), - season = it.season_number - ) - } - }?.flatten() ?: listOf() + it.episode_number, + TmdbLink( + it.external_ids?.imdb_id ?: this.external_ids?.imdb_id, + this.id, + it.episode_number, + it.season_number, + ).toJson(), + getImageUrl(it.still_path), + it.air_date?.toString(), + it.rating, + it.overview, + ) + } ?: (1..(it.episode_count ?: 1)).map { episodeNum -> + 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( this.name ?: this.original_name, getUrl(id, true), @@ -112,18 +124,17 @@ open class TmdbProvider : MainAPI() { }.get(Calendar.YEAR) }, this.overview, - null,//this.status - null, // possible to get + null, // this.status + this.external_ids?.imdb_id, this.rating, this.genres?.mapNotNull { it.name }, - null, //this.episode_run_time.average() + this.episode_run_time?.average()?.times(60)?.toInt()?.let { secondsToReadable(it, "") }, null, this.recommendations?.results?.map { it.toSearchResponse() } ) } private fun Movie.toLoadResponse(): MovieLoadResponse { - println("EXTERNAL IDS ${this.toJson()}") return MovieLoadResponse( this.title ?: this.original_title, getUrl(id, true), @@ -145,7 +156,7 @@ open class TmdbProvider : MainAPI() { null,//this.status this.rating, this.genres?.mapNotNull { it.name }, - null, //this.episode_run_time.average() + this.runtime?.times(60)?.let { secondsToReadable(it, "") }, null, this.recommendations?.results?.map { it.toSearchResponse() } ) diff --git a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersTwoProvider.kt b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersTwoProvider.kt index 6948da08..c6ac1d12 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersTwoProvider.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/movieproviders/TrailersTwoProvider.kt @@ -2,8 +2,11 @@ package com.lagradost.cloudstream3.movieproviders import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.module.kotlin.readValue +import com.lagradost.cloudstream3.ProviderType import com.lagradost.cloudstream3.SubtitleFile +import com.lagradost.cloudstream3.TvType import com.lagradost.cloudstream3.mapper +import com.lagradost.cloudstream3.metaproviders.TmdbLink import com.lagradost.cloudstream3.metaproviders.TmdbProvider import com.lagradost.cloudstream3.network.get import com.lagradost.cloudstream3.network.text @@ -30,6 +33,15 @@ class TrailersTwoProvider : TmdbProvider() { override val instantLinkLoading: Boolean get() = true + override val supportedTypes: Set + get() = setOf( + TvType.Movie, + TvType.TvSeries, + TvType.AnimeMovie, + TvType.Anime, + TvType.Cartoon + ) + override fun loadLinks( data: String, isCasting: Boolean, @@ -37,8 +49,10 @@ class TrailersTwoProvider : TmdbProvider() { callback: (ExtractorLink) -> Unit ): Boolean { val mappedData = mapper.readValue(data) - println("MAPPED $mappedData") - if (mappedData.imdbID == null) return false + 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) { @@ -46,25 +60,25 @@ class TrailersTwoProvider : TmdbProvider() { ExtractorLink( this.name, this.name, - "https://trailers.to/video/$user/imdb/${mappedData.imdbID}", + "https://trailers.to/video/$user/$site/$id", "https://trailers.to", Qualities.Unknown.value, false, ) ) - "https://trailers.to/subtitles/$user/imdb/${mappedData.imdbID}" + "https://trailers.to/subtitles/$user/$site/$id" } else { callback.invoke( ExtractorLink( 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", Qualities.Unknown.value, 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 = @@ -73,9 +87,9 @@ class TrailersTwoProvider : TmdbProvider() { subtitlesMapped.forEach { subtitleCallback.invoke( 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"}" - ).also { println(it) } + ) ) } return true diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index 46edf989..9f6d326b 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -927,6 +927,12 @@ class ResultFragment : Fragment() { } 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) currentHeaderName = d.name diff --git a/app/src/main/res/layout/fragment_result.xml b/app/src/main/res/layout/fragment_result.xml index d030c00a..32c50ad3 100644 --- a/app/src/main/res/layout/fragment_result.xml +++ b/app/src/main/res/layout/fragment_result.xml @@ -275,6 +275,12 @@ tools:text="@string/vpn_torrent" android:layout_width="match_parent" android:layout_height="wrap_content"> + + More Info A VPN might be needed for this provider to work correctly - This providers is a torrent, a VPN is recommended + This provider is a torrent, a VPN is recommended + + Metadata is not provided by site, video loading might fail. + Description No Plot Found No Description Found